home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’97 / ContextWarrior / source code / MoreFiles 1.4.6 / Sources / MoreFilesExtras.c
Text File  |  1997-02-23  |  82KB  |  2,918 lines

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    A collection of useful high-level File Manager routines.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support Emeritus
  7. **
  8. **    File:        MoreFilesExtras.c
  9. **
  10. **    Copyright © 1992-1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Traps.h>
  24. #include <OSUtils.h>
  25. #include <Errors.h>
  26. #include <Files.h>
  27. #include <Devices.h>
  28. #include <Finder.h>
  29. #include <Folders.h>
  30. #include <FSM.h>
  31. #include <Disks.h>
  32. #include <Gestalt.h>
  33. #include <TextUtils.h>
  34. #include <Script.h>
  35.  
  36. #define    __COMPILINGMOREFILES
  37.  
  38. #include "MoreFiles.h"
  39. #include "MoreFilesExtras.h"
  40. #include "MoreDesktopMgr.h"
  41. #include "FSpCompat.h"
  42.  
  43. /*****************************************************************************/
  44.  
  45. /* local data structures */
  46.  
  47. /* The DeleteEnumGlobals structure is used to minimize the amount of
  48. ** stack space used when recursively calling DeleteLevel and to hold
  49. ** global information that might be needed at any time. */
  50.  
  51. #if PRAGMA_ALIGN_SUPPORTED
  52. #pragma options align=mac68k
  53. #endif
  54. struct DeleteEnumGlobals
  55. {
  56.     OSErr            error;                /* temporary holder of results - saves 2 bytes of stack each level */
  57.     Str63            itemName;            /* the name of the current item */
  58.     UniversalFMPB    myPB;                /* the parameter block used for PBGetCatInfo calls */
  59. };
  60. #if PRAGMA_ALIGN_SUPPORTED
  61. #pragma options align=reset
  62. #endif
  63.  
  64. typedef struct DeleteEnumGlobals DeleteEnumGlobals;
  65. typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr;
  66.  
  67. /*****************************************************************************/
  68.  
  69. pascal    void    TruncPString(StringPtr destination,
  70.                              ConstStr255Param source,
  71.                              short maxLength)
  72. {
  73.     short    charType;
  74.     
  75.     if ( source != NULL && destination != NULL )    /* don't do anything stupid */
  76.     {
  77.         if ( source[0] > maxLength )
  78.         {
  79.             /* Make sure the string isn't truncated in the middle of */
  80.             /* a multi-byte character. */
  81.             while (maxLength != 0)
  82.             {
  83.                 charType = CharByte((Ptr)&source[1], maxLength);
  84.                 if ( (charType == smSingleByte) || (charType == smLastByte) )
  85.                     break;    /* source[maxLength] is now a valid last character */ 
  86.                 --maxLength;
  87.             }
  88.         }
  89.         else
  90.         {
  91.             maxLength = source[0];
  92.         }
  93.         /* Set the destination string length */
  94.         destination[0] = maxLength;
  95.         /* and copy maxLength characters (if needed) */
  96.         if ( source != destination )
  97.         {
  98.             while ( maxLength != 0 )
  99.             {
  100.                 destination[maxLength] = source[maxLength];
  101.                 --maxLength;
  102.             }
  103.         }
  104.     }
  105. }
  106.  
  107. /*****************************************************************************/
  108.  
  109. pascal    Ptr    GetTempBuffer(long buffReqSize,
  110.                           long *buffActSize)
  111. {
  112.     enum
  113.     {
  114.         kSlopMemory = 0x00008000    /* 32K - Amount of free memory to leave when allocating buffers */
  115.     };
  116.     Ptr    tempPtr;
  117.     
  118.     /* Make request a multiple of 1024 bytes */
  119.     buffReqSize = buffReqSize & 0xfffffc00;
  120.     
  121.     if ( buffReqSize < 0x00000400 )
  122.     {
  123.         /* Request was smaller than 1024 bytes - make it 1024 */
  124.         buffReqSize = 0x00000400;
  125.     }
  126.     
  127.     /* Attempt to allocate the memory */
  128.     tempPtr = NewPtr(buffReqSize);
  129.     
  130.     /* If request failed, go to backup plan */
  131.     if ( (tempPtr == NULL) && (buffReqSize > 0x00000400) )
  132.     {
  133.         /*
  134.         **    Try to get largest 1024-byte block available
  135.         **    leaving some slop for the toolbox if possible
  136.         */
  137.         long freeMemory = (FreeMem() - kSlopMemory) & 0xfffffc00;
  138.         
  139.         buffReqSize = MaxBlock() & 0xfffffc00;
  140.         
  141.         if ( buffReqSize > freeMemory )
  142.         {
  143.             buffReqSize = freeMemory;
  144.         }
  145.         
  146.         if ( buffReqSize == 0 )
  147.         {
  148.             buffReqSize = 0x00000400;
  149.         }
  150.         
  151.         tempPtr = NewPtr(buffReqSize);
  152.     }
  153.     
  154.     /* Return bytes allocated */
  155.     if ( tempPtr != NULL )
  156.     {
  157.         *buffActSize = buffReqSize;
  158.     }
  159.     else
  160.     {
  161.         *buffActSize = 0;
  162.     }
  163.     
  164.     return ( tempPtr );
  165. }
  166.  
  167. /*****************************************************************************/
  168.  
  169. /*
  170. **    GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync
  171. **    in cases where the returned volume name is not needed by the caller.
  172. **    The pathname and vRefNum parameters are not touched, and the pb
  173. **    parameter is initialized by PBHGetVInfoSync except that ioNamePtr in
  174. **    the parameter block is always returned as NULL (since it might point
  175. **    to the local tempPathname).
  176. **
  177. **    I noticed using this code in several places, so here it is once.
  178. **    This reduces the code size of MoreFiles.
  179. */
  180. pascal    OSErr    GetVolumeInfoNoName(ConstStr255Param pathname,
  181.                                     short vRefNum,
  182.                                     HParmBlkPtr pb)
  183. {
  184.     Str255 tempPathname;
  185.     OSErr error;
  186.     
  187.     /* Make sure pb parameter is not NULL */ 
  188.     if ( pb != NULL )
  189.     {
  190.         pb->volumeParam.ioVRefNum = vRefNum;
  191.         if ( pathname == NULL )
  192.         {
  193.             pb->volumeParam.ioNamePtr = NULL;
  194.             pb->volumeParam.ioVolIndex = 0;        /* use ioVRefNum only */
  195.         }
  196.         else
  197.         {
  198.             BlockMoveData(pathname, tempPathname, pathname[0] + 1);    /* make a copy of the string and */
  199.             pb->volumeParam.ioNamePtr = (StringPtr)tempPathname;    /* use the copy so original isn't trashed */
  200.             pb->volumeParam.ioVolIndex = -1;    /* use ioNamePtr/ioVRefNum combination */
  201.         }
  202.         error = PBHGetVInfoSync(pb);
  203.         pb->volumeParam.ioNamePtr = NULL;    /* ioNamePtr may point to local    tempPathname, so don't return it */
  204.     }
  205.     else
  206.     {
  207.         error = paramErr;
  208.     }
  209.     return ( error );
  210. }
  211.  
  212. /*****************************************************************************/
  213.  
  214. pascal    OSErr GetCatInfoNoName(short vRefNum,
  215.                                long dirID,
  216.                                ConstStr255Param name,
  217.                                CInfoPBPtr pb)
  218. {
  219.     Str31 tempName;
  220.     OSErr error;
  221.     
  222.     /* Protection against File Sharing problem */
  223.     if ( (name == NULL) || (name[0] == 0) )
  224.     {
  225.         tempName[0] = 0;
  226.         pb->dirInfo.ioNamePtr = tempName;
  227.         pb->dirInfo.ioFDirIndex = -1;    /* use ioDirID */
  228.     }
  229.     else
  230.     {
  231.         pb->dirInfo.ioNamePtr = (StringPtr)name;
  232.         pb->dirInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  233.     }
  234.     pb->dirInfo.ioVRefNum = vRefNum;
  235.     pb->dirInfo.ioDrDirID = dirID;
  236.     error = PBGetCatInfoSync(pb);
  237.     pb->dirInfo.ioNamePtr = NULL;
  238.     return ( error );
  239. }
  240.  
  241. /*****************************************************************************/
  242.  
  243. pascal    OSErr    DetermineVRefNum(ConstStr255Param pathname,
  244.                                  short vRefNum,
  245.                                  short *realVRefNum)
  246. {
  247.     HParamBlockRec pb;
  248.     OSErr error;
  249.  
  250.     error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
  251.     *realVRefNum = pb.volumeParam.ioVRefNum;
  252.     return ( error );
  253. }
  254.  
  255. /*****************************************************************************/
  256.  
  257. pascal    OSErr    HGetVInfo(short volReference,
  258.                           StringPtr volName,
  259.                           short *vRefNum,
  260.                           unsigned long *freeBytes,
  261.                           unsigned long *totalBytes)
  262. {
  263.     HParamBlockRec    pb;
  264.     unsigned long    allocationBlockSize;
  265.     unsigned short    numAllocationBlocks;
  266.     unsigned short    numFreeBlocks;
  267.     VCB                *theVCB;
  268.     Boolean            vcbFound;
  269.     OSErr            result;
  270.     
  271.     /* Use the File Manager to get the real vRefNum */
  272.     pb.volumeParam.ioVRefNum = volReference;
  273.     pb.volumeParam.ioNamePtr = volName;
  274.     pb.volumeParam.ioVolIndex = 0;    /* use ioVRefNum only, return volume name */
  275.     result = PBHGetVInfoSync(&pb);
  276.     
  277.     if ( result == noErr )
  278.     {
  279.         /* The volume name was returned in volName (if not NULL) and */
  280.         /* we have the volume's vRefNum and allocation block size */
  281.         *vRefNum = pb.volumeParam.ioVRefNum;
  282.         allocationBlockSize = (unsigned long)pb.volumeParam.ioVAlBlkSiz;
  283.         
  284.         /* System 7.5 (and beyond) pins the number of allocation blocks and */
  285.         /* the number of free allocation blocks returned by PBHGetVInfo to */
  286.         /* a value so that when multiplied by the allocation block size, */
  287.         /* the volume will look like it has $7fffffff bytes or less. This */
  288.         /* was done so older applications that use signed math or that use */
  289.         /* the GetVInfo function (which uses signed math) will continue to work. */
  290.         /* However, the unpinned numbers (which we want) are always available */
  291.         /* in the volume's VCB so we'll get those values from the VCB if possible. */
  292.         
  293.         /* Find the volume's VCB */
  294.         vcbFound = false;
  295.         theVCB = (VCB *)(GetVCBQHdr()->qHead);
  296.         while ( (theVCB != NULL) && !vcbFound )
  297.         {
  298.             /* Check VCB signature before using VCB. Don't have to check for */
  299.             /* MFS (0xd2d7) because they can't get big enough to be pinned */
  300.             if ( theVCB->vcbSigWord == 0x4244 )
  301.             {
  302.                 if ( theVCB->vcbVRefNum == *vRefNum )
  303.                 {
  304.                     vcbFound = true;
  305.                 }
  306.             }
  307.             
  308.             if ( !vcbFound )
  309.             {
  310.                 theVCB = (VCB *)(theVCB->qLink);
  311.             }
  312.         }
  313.         
  314.         if ( theVCB != NULL )
  315.         {
  316.             /* Found a VCB we can use. Get the un-pinned number of allocation blocks */
  317.             /* and the number of free blocks from the VCB. */
  318.             numAllocationBlocks = (unsigned short)theVCB->vcbNmAlBlks;
  319.             numFreeBlocks = (unsigned short)theVCB->vcbFreeBks;
  320.         }
  321.         else
  322.         {
  323.             /* Didn't find a VCB we can use. Return the number of allocation blocks */
  324.             /* and the number of free blocks returned by PBHGetVInfoSync. */
  325.             numAllocationBlocks = (unsigned short)pb.volumeParam.ioVNmAlBlks;
  326.             numFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk;
  327.         }
  328.         
  329.         /* Now, calculate freeBytes and totalBytes using unsigned values */
  330.         *freeBytes = numFreeBlocks * allocationBlockSize;
  331.         *totalBytes = numAllocationBlocks * allocationBlockSize;
  332.     }
  333.     
  334.     return ( result );
  335. }
  336.  
  337. /*****************************************************************************/
  338.  
  339. /*
  340. **    PBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync
  341. **    File Manager requests from CFM-based programs. At some point, Apple
  342. **    will get around to adding this to the standard libraries you link with
  343. **    and you'll get a duplicate symbol link error. At that time, just delete
  344. **    this code (or comment it out).
  345. **
  346. **    Non-CFM 68K programs don't needs this glue (and won't get it) because
  347. **    they instead use the inline assembly glue found in the Files.h interface
  348. **    file.
  349. */
  350.  
  351. #if    __WANTPASCALELIMINATION
  352. #undef    pascal
  353. #endif
  354.  
  355. #if GENERATINGCFM
  356. pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock)
  357. {
  358.     enum
  359.     {
  360.         kXGetVolInfoSelector = 0x0012,    /* Selector for XGetVolInfo */
  361.         
  362.         uppFSDispatchProcInfo = kRegisterBased
  363.              | REGISTER_RESULT_LOCATION(kRegisterD0)
  364.              | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
  365.              | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(long)))    /* trap word */
  366.              | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(long)))    /* selector */
  367.              | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0, SIZE_CODE(sizeof(XVolumeParamPtr)))
  368.     };
  369.     
  370.     return ( CallOSTrapUniversalProc(NGetTrapAddress(_FSDispatch, OSTrap),
  371.                                         uppFSDispatchProcInfo,
  372.                                         _FSDispatch,
  373.                                         kXGetVolInfoSelector,
  374.                                         paramBlock) );
  375. }
  376. #endif
  377.  
  378. #if    __WANTPASCALELIMINATION
  379. #define    pascal    
  380. #endif
  381.  
  382. /*****************************************************************************/
  383.  
  384. pascal    OSErr    XGetVInfo(short volReference,
  385.                           StringPtr volName,
  386.                           short *vRefNum,
  387.                           UnsignedWide *freeBytes,
  388.                           UnsignedWide *totalBytes)
  389. {
  390.     OSErr            result;
  391.     long            response;
  392.     XVolumeParam    pb;
  393.     
  394.     /* See if large volume support is available */
  395.     if ( ( Gestalt(gestaltFSAttr, &response) == noErr ) && ((response & (1L << gestaltFSSupports2TBVols)) != 0) )
  396.     {
  397.         /* Large volume support is available */
  398.         pb.ioVRefNum = volReference;
  399.         pb.ioNamePtr = volName;
  400.         pb.ioXVersion = 0;    /* this XVolumeParam version (0) */
  401.         pb.ioVolIndex = 0;    /* use ioVRefNum only, return volume name */
  402.         result = PBXGetVolInfoSync(&pb);
  403.         if ( result == noErr )
  404.         {
  405.             /* The volume name was returned in volName (if not NULL) and */
  406.             /* we have the volume's vRefNum and allocation block size */
  407.             *vRefNum = pb.ioVRefNum;
  408.             
  409.             /* return the freeBytes and totalBytes */
  410.             *totalBytes = pb.ioVTotalBytes;
  411.             *freeBytes = pb.ioVFreeBytes;
  412.         }
  413.     }
  414.     else
  415.     {
  416.         /* No large volume support */
  417.         
  418.         /* zero the high longs of totalBytes and freeBytes */
  419.         totalBytes->hi = 0;
  420.         freeBytes->hi = 0;
  421.         
  422.         /* Use HGetVInfo to get the results */
  423.         result = HGetVInfo(volReference, volName, vRefNum, &freeBytes->lo, &totalBytes->lo);
  424.     }
  425.     return ( result );
  426. }
  427.  
  428. /*****************************************************************************/
  429.  
  430. pascal    OSErr    CheckVolLock(ConstStr255Param pathname,
  431.                              short vRefNum)
  432. {
  433.     HParamBlockRec pb;
  434.     OSErr error;
  435.  
  436.     error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
  437.     if ( error == noErr )
  438.     {
  439.         if ( (pb.volumeParam.ioVAtrb & 0x0080) != 0 )
  440.         {
  441.             error = wPrErr;        /* volume locked by hardware */
  442.         }
  443.         else if ( (pb.volumeParam.ioVAtrb & 0x8000) != 0 )
  444.         {
  445.             error = vLckdErr;    /* volume locked by software */
  446.         }
  447.     }
  448.     
  449.     return ( error );
  450. }
  451.  
  452. /*****************************************************************************/
  453.  
  454. pascal    OSErr GetDriverName(short driverRefNum,
  455.                             Str255 driverName)
  456. {
  457.     OSErr result;
  458.     DCtlHandle theDctl;
  459.     DRVRHeaderPtr dHeaderPtr;
  460.     
  461.     theDctl = GetDCtlEntry(driverRefNum);
  462.     if ( theDctl != NULL )
  463.     {
  464.         if ( (**theDctl).dCtlFlags & 0x40 )
  465.         {
  466.             /* dctlDriver is handle - dereference */
  467.             dHeaderPtr = *((DRVRHeaderHandle)(**theDctl).dCtlDriver);
  468.         }
  469.         else
  470.         {
  471.             /* dctlDriver is pointer */
  472.           dHeaderPtr = (DRVRHeaderPtr)(**theDctl).dCtlDriver;
  473.         }
  474.         BlockMoveData((*dHeaderPtr).drvrName, driverName, (*dHeaderPtr).drvrName[0] + 1);
  475.         result = noErr;
  476.     }
  477.     else
  478.     {
  479.         driverName[0] = 0;
  480.         result = badUnitErr;    /* bad reference number */
  481.     }
  482.     
  483.     return ( result );
  484. }
  485.  
  486. /*****************************************************************************/
  487.  
  488. pascal    OSErr    FindDrive(ConstStr255Param pathname,
  489.                           short vRefNum,
  490.                           DrvQElPtr *driveQElementPtr)
  491. {
  492.     OSErr            result;
  493.     HParamBlockRec    hPB;
  494.     short            driveNumber;
  495.     
  496.     *driveQElementPtr = NULL;
  497.     
  498.     /* First, use GetVolumeInfoNoName to determine the volume */
  499.     result = GetVolumeInfoNoName(pathname, vRefNum, &hPB);
  500.     if ( result == noErr )
  501.     {
  502.         /*
  503.         **    The volume can be either online, offline, or ejected. What we find in
  504.         **    ioVDrvInfo and ioVDRefNum will tell us which it is.
  505.         **    See Inside Macintosh: Files page 2-80 and the Technical Note
  506.         **    "FL 34 - VCBs and Drive Numbers : The Real Story"
  507.         **    Where we get the drive number depends on the state of the volume.
  508.         */
  509.         if ( hPB.volumeParam.ioVDrvInfo != 0 )
  510.         {
  511.             /* The volume is online and not ejected */
  512.             /* Get the drive number */
  513.             driveNumber = hPB.volumeParam.ioVDrvInfo;
  514.         }
  515.         else
  516.         {
  517.             /* The volume's is either offline or ejected */
  518.             /* in either case, the volume is NOT online */
  519.  
  520.             /* Is it ejected or just offline? */
  521.             if ( hPB.volumeParam.ioVDRefNum > 0 )
  522.             {
  523.                 /* It's ejected, the drive number is ioVDRefNum */
  524.                 driveNumber = hPB.volumeParam.ioVDRefNum;
  525.             }
  526.             else
  527.             {
  528.                 /* It's offline, the drive number is the negative of ioVDRefNum */
  529.                 driveNumber = (short)-hPB.volumeParam.ioVDRefNum;
  530.             }
  531.         }
  532.         
  533.         /* Get pointer to first element in drive queue */
  534.         *driveQElementPtr = (DrvQElPtr)(GetDrvQHdr()->qHead);
  535.         
  536.         /* Search for a matching drive number */
  537.         while ( (*driveQElementPtr != NULL) && ((*driveQElementPtr)->dQDrive != driveNumber) )
  538.         {
  539.             *driveQElementPtr = (DrvQElPtr)(*driveQElementPtr)->qLink;
  540.         }
  541.         
  542.         if ( *driveQElementPtr == NULL )
  543.         {
  544.             /* This should never happen since every volume must have a drive, but... */
  545.             result = nsDrvErr;
  546.         }
  547.     }
  548.     
  549.     return ( result );
  550. }
  551.  
  552. /*****************************************************************************/
  553.  
  554. pascal    OSErr    GetDiskBlocks(ConstStr255Param pathname,
  555.                               short vRefNum,
  556.                               unsigned long *numBlocks)
  557. {
  558.     /* Various constants for GetDiskBlocks() */
  559.     enum
  560.     {
  561.         /* return format list status code */
  562.         kFmtLstCode = 6,
  563.         
  564.         /* reference number of .SONY driver */
  565.         kSonyRefNum = 0xfffb,
  566.         
  567.         /* values returned by DriveStatus in DrvSts.twoSideFmt */
  568.         kSingleSided = 0,
  569.         kDoubleSided = -1,
  570.         kSingleSidedSize = 800,        /* 400K */
  571.         kDoubleSidedSize = 1600,    /* 800K */
  572.         
  573.         /* values in DrvQEl.qType */
  574.         kWordDrvSiz = 0,
  575.         kLongDrvSiz = 1,
  576.         
  577.         /* more than enough formatListRecords */
  578.         kMaxFormatListRecs = 16
  579.     };
  580.     
  581.     DrvQElPtr        driveQElementPtr;
  582.     unsigned long    blocks;
  583.     ParamBlockRec    pb;
  584.     FormatListRec    formatListRecords[kMaxFormatListRecs];
  585.     DrvSts            status;
  586.     short            formatListRecIndex;
  587.     OSErr            result;
  588.  
  589.     blocks = 0;
  590.     
  591.     /* Find the drive queue element for this volume */
  592.     result = FindDrive(pathname, vRefNum, &driveQElementPtr);
  593.     
  594.     /* 
  595.     **    Make sure this is a real driver (dQRefNum < 0).
  596.     **    AOCE's Mail Enclosures volume uses 0 for dQRefNum which will cause
  597.     **    problems if you try to use it as a driver refNum.
  598.     */ 
  599.     if ( (result == noErr) && (driveQElementPtr->dQRefNum >= 0) )
  600.     {
  601.         result = paramErr;
  602.     }
  603.     else
  604.     {
  605.         /* Attempt to get the drive's format list. */
  606.         /* (see the Technical Note "What Your Sony Drives For You") */
  607.         
  608.         pb.cntrlParam.ioVRefNum = driveQElementPtr->dQDrive;
  609.         pb.cntrlParam.ioCRefNum = driveQElementPtr->dQRefNum;
  610.         pb.cntrlParam.csCode = kFmtLstCode;
  611.         pb.cntrlParam.csParam[0] = kMaxFormatListRecs;
  612.         *(long *)&pb.cntrlParam.csParam[1] = (long)&formatListRecords[0];
  613.         
  614.         result = PBStatusSync(&pb);
  615.         
  616.         if ( result == noErr )
  617.         {
  618.             /* The drive supports ReturnFormatList status call. */
  619.             
  620.             /* Get the current disk's size. */
  621.             for( formatListRecIndex = 0;
  622.                  formatListRecIndex < pb.cntrlParam.csParam[0];
  623.                  ++formatListRecIndex )
  624.             {
  625.                 if ( (formatListRecords[formatListRecIndex].formatFlags &
  626.                       diCIFmtFlagsCurrentMask) != 0 )
  627.                 {
  628.                     blocks = formatListRecords[formatListRecIndex].volSize;
  629.                 }
  630.             }
  631.             if ( blocks == 0 )
  632.             {
  633.                 /* This should never happen */
  634.                 result = paramErr;
  635.             }
  636.         }
  637.         else if ( driveQElementPtr->dQRefNum == (short)kSonyRefNum )
  638.         {
  639.             /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */
  640.             
  641.             result = DriveStatus(driveQElementPtr->dQDrive, &status);
  642.             if ( result == noErr )
  643.             {
  644.                 switch ( status.twoSideFmt )
  645.                 {
  646.                 case kSingleSided:
  647.                     blocks = kSingleSidedSize;
  648.                     break;
  649.                 case kDoubleSided:
  650.                     blocks = kDoubleSidedSize;
  651.                     break;
  652.                 default:
  653.                     /* This should never happen */
  654.                     result = paramErr;
  655.                     break;
  656.                 }
  657.             }
  658.         }
  659.         else
  660.         {
  661.             /* The drive is not a floppy and it doesn't support ReturnFormatList */
  662.             /* so use the dQDrvSz field(s) */
  663.             
  664.             result = noErr;    /* reset result */
  665.             switch ( driveQElementPtr->qType )
  666.             {
  667.             case kWordDrvSiz:
  668.                 blocks = driveQElementPtr->dQDrvSz;
  669.                 break;
  670.             case kLongDrvSiz:
  671.                 blocks = ((unsigned long)driveQElementPtr->dQDrvSz2 << 16) +
  672.                          driveQElementPtr->dQDrvSz;
  673.                 break;
  674.             default:
  675.                 /* This should never happen */
  676.                 result = paramErr;
  677.                 break;
  678.             }
  679.         }
  680.     }
  681.     
  682.     *numBlocks = blocks;
  683.     
  684.     return ( result );
  685. }
  686.  
  687. /*****************************************************************************/
  688.  
  689. pascal    OSErr    GetVolFileSystemID(ConstStr255Param pathname,
  690.                                    short vRefNum,
  691.                                    short *fileSystemID)
  692. {
  693.     HParamBlockRec pb;
  694.     OSErr error;
  695.  
  696.     error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
  697.     if ( error == noErr )
  698.     {
  699.         *fileSystemID = pb.volumeParam.ioVFSID;
  700.     }
  701.     
  702.     return ( error );
  703. }
  704.  
  705. /*****************************************************************************/
  706.  
  707. pascal    OSErr    GetVolState(ConstStr255Param pathname,
  708.                             short vRefNum,
  709.                             Boolean *volumeOnline,
  710.                             Boolean *volumeEjected,
  711.                             Boolean *driveEjectable,
  712.                             Boolean *driverWantsEject)
  713. {
  714.     HParamBlockRec pb;
  715.     short driveNumber;
  716.     OSErr error;
  717.  
  718.     error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
  719.     if ( error == noErr )
  720.     {
  721.         if ( pb.volumeParam.ioVDrvInfo != 0 )
  722.         {
  723.             /* the volume is online and not ejected */
  724.             *volumeOnline = true;
  725.             *volumeEjected = false;
  726.             
  727.             /* Get the drive number */
  728.             driveNumber = pb.volumeParam.ioVDrvInfo;
  729.         }
  730.         else
  731.         {
  732.             /* the volume's is either offline or ejected */
  733.             /* in either case, the volume is NOT online */
  734.             *volumeOnline = false;
  735.  
  736.             /* Is it ejected? */
  737.             *volumeEjected = pb.volumeParam.ioVDRefNum > 0;
  738.             
  739.             if ( *volumeEjected )
  740.             {
  741.                 /* If ejected, the drive number is ioVDRefNum */
  742.                 driveNumber = pb.volumeParam.ioVDRefNum;
  743.             }
  744.             else
  745.             {
  746.                 /* If offline, the drive number is the negative of ioVDRefNum */
  747.                 driveNumber = (short)-pb.volumeParam.ioVDRefNum;
  748.             }
  749.         }
  750.         
  751.         {
  752.             DrvQElPtr drvQElem;
  753.             
  754.             /* Find the drive queue element by searching the drive queue */
  755.             drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead);
  756.             while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNumber) )
  757.             {
  758.                 drvQElem = (DrvQElPtr)drvQElem->qLink;
  759.             }
  760.             
  761.             if ( drvQElem != NULL )
  762.             {
  763.                 /*
  764.                 **    Each drive queue element is preceded by 4 flag bytes.
  765.                 **    Byte 1 (the second flag byte) has bits that tell us if a
  766.                 **    drive is ejectable and if its driver wants an eject call.
  767.                 **    See Inside Macintosh: Files, page 2-85.
  768.                 */
  769.                 {
  770.                     Ptr        flagBytePtr;
  771.                     
  772.                     /* point to byte 1 of the flag bytes */
  773.                     flagBytePtr = (Ptr)drvQElem;
  774.                     flagBytePtr -= 3;
  775.                     
  776.                     /*
  777.                     **    The drive is ejectable if flag byte 1 does not contain
  778.                     **    0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call).
  779.                     */
  780.                     
  781.                     *driveEjectable = (*flagBytePtr != 0x08) && (*flagBytePtr != 0x48);
  782.                     
  783.                     /*
  784.                     **    The driver wants an eject call if flag byte 1 does not contain
  785.                     **    0x08 (nonejectable). This may seem like a minor point, but some
  786.                     **    disk drivers use the Eject request to flush their caches to disk
  787.                     **    and you wouldn't want to skip that step after unmounting a volume.
  788.                     */
  789.                     
  790.                     *driverWantsEject = (*flagBytePtr != 0x08);
  791.                 }
  792.             }
  793.             else
  794.             {
  795.                 /* Didn't find the drive (this should never happen) */
  796.                 *driveEjectable = false;
  797.                 *driverWantsEject = false;
  798.             }
  799.         }
  800.     }
  801.     
  802.     return ( error );
  803. }
  804.  
  805. /*****************************************************************************/
  806.  
  807. pascal    OSErr    UnmountAndEject(ConstStr255Param pathname,
  808.                                 short vRefNum)
  809. {
  810.     HParamBlockRec pb;
  811.     short driveNum;
  812.     Boolean ejected, wantsEject;
  813.     DrvQElPtr drvQElem;
  814.     OSErr error;
  815.  
  816.     error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
  817.     if ( error == noErr )
  818.     {
  819.         if ( pb.volumeParam.ioVDrvInfo != 0 )
  820.         {
  821.             /* the volume is online and not ejected */
  822.             ejected = false;
  823.             
  824.             /* Get the drive number */
  825.             driveNum = pb.volumeParam.ioVDrvInfo;
  826.         }
  827.         else
  828.         {
  829.             /* the volume is ejected or offline */
  830.             
  831.             /* Is it ejected? */
  832.             ejected = pb.volumeParam.ioVDRefNum > 0;
  833.             
  834.             if ( ejected )
  835.             {
  836.                 /* If ejected, the drive number is ioVDRefNum */
  837.                 driveNum = pb.volumeParam.ioVDRefNum;
  838.             }
  839.             else
  840.             {
  841.                 /* If offline, the drive number is the negative of ioVDRefNum */
  842.                 driveNum = (short)-pb.volumeParam.ioVDRefNum;
  843.             }
  844.         }
  845.         
  846.         /* find the drive queue element */
  847.         drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead);
  848.         while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNum) )
  849.         {
  850.             drvQElem = (DrvQElPtr)drvQElem->qLink;
  851.         }
  852.         
  853.         if ( drvQElem != NULL )
  854.         {
  855.             /* does the drive want an eject call */
  856.             wantsEject = (*((Ptr)((Ptr)drvQElem - 3)) != 8);
  857.         }
  858.         else
  859.         {
  860.             /* didn't find the drive!! */
  861.             wantsEject = false;
  862.         }
  863.         
  864.         /* unmount the volume */
  865.         pb.volumeParam.ioNamePtr = NULL;
  866.         /* ioVRefNum is already filled in from PBHGetVInfo */
  867.         error = PBUnmountVol((ParmBlkPtr)&pb);
  868.         if ( error == noErr )
  869.         {
  870.             if ( wantsEject && !ejected )
  871.             {
  872.                 /* eject the media from the drive if needed */
  873.                 pb.volumeParam.ioVRefNum = driveNum;
  874.                 error = PBEject((ParmBlkPtr)&pb);
  875.             }
  876.         }
  877.     }
  878.     
  879.     return ( error );
  880. }
  881.  
  882. /*****************************************************************************/
  883.  
  884. pascal    OSErr    OnLine(FSSpecPtr volumes,
  885.                        short reqVolCount,
  886.                        short *actVolCount,
  887.                        short *volIndex)
  888. {
  889.     HParamBlockRec pb;
  890.     OSErr error = noErr;
  891.     FSSpec *endVolArray;
  892.  
  893.     if ( *volIndex > 0 )
  894.     {
  895.         *actVolCount = 0;
  896.         for ( endVolArray = volumes + reqVolCount; (volumes < endVolArray) && (error == noErr); ++volumes )
  897.         {
  898.             pb.volumeParam.ioNamePtr = (StringPtr) & volumes->name;
  899.             pb.volumeParam.ioVolIndex = *volIndex;
  900.             error = PBHGetVInfoSync(&pb);
  901.             if ( error == noErr )
  902.             {
  903.                 volumes->parID = fsRtParID;        /* the root directory's parent is 1 */
  904.                 volumes->vRefNum = pb.volumeParam.ioVRefNum;
  905.                 ++*volIndex;
  906.                 ++*actVolCount;
  907.             }
  908.         }
  909.     }
  910.     else
  911.     {
  912.         error = paramErr;
  913.     }
  914.     
  915.     return ( error );
  916. }
  917.  
  918. /*****************************************************************************/
  919.  
  920. pascal    OSErr SetDefault(short newVRefNum,
  921.                          long newDirID,
  922.                          short *oldVRefNum,
  923.                          long *oldDirID)
  924. {
  925.     OSErr    error;
  926.     
  927.     /* Get the current default volume/directory. */
  928.     error = HGetVol(NULL, oldVRefNum, oldDirID);
  929.     if ( error == noErr )
  930.     {
  931.         /* Set the new default volume/directory */
  932.         error = HSetVol(NULL, newVRefNum, newDirID);
  933.     }
  934.     
  935.     return ( error );
  936. }
  937.  
  938. /*****************************************************************************/
  939.  
  940. pascal    OSErr RestoreDefault(short oldVRefNum,
  941.                              long oldDirID)
  942. {
  943.     OSErr    error;
  944.     short    defaultVRefNum;
  945.     long    defaultDirID;
  946.     long    defaultProcID;
  947.     
  948.     /* Determine if the default volume was a wdRefNum. */
  949.     error = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID);
  950.     if ( error == noErr )
  951.     {
  952.         /* Restore the old default volume/directory, one way or the other. */
  953.         if ( defaultDirID != fsRtDirID )
  954.         {
  955.             /* oldVRefNum was a wdRefNum - use SetVol */
  956.             error = SetVol(NULL, oldVRefNum);
  957.         }
  958.         else
  959.         {
  960.             /* oldVRefNum was a real vRefNum - use HSetVol */
  961.             error = HSetVol(NULL, oldVRefNum, oldDirID);
  962.         }
  963.     }
  964.     
  965.     return ( error );
  966. }
  967.  
  968. /*****************************************************************************/
  969.  
  970. pascal    OSErr GetDInfo(short vRefNum,
  971.                        long dirID,
  972.                        ConstStr255Param name,
  973.                        DInfo *fndrInfo)
  974. {
  975.     CInfoPBRec pb;
  976.     OSErr error;
  977.     
  978.     error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
  979.     if ( error == noErr )
  980.     {
  981.         if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  982.         {
  983.             /* it's a directory, return the DInfo */
  984.             *fndrInfo = pb.dirInfo.ioDrUsrWds;
  985.         }
  986.         else
  987.         {
  988.             /* oops, a file was passed */
  989.             error = dirNFErr;
  990.         }
  991.     }
  992.     
  993.     return ( error );
  994. }
  995.  
  996. /*****************************************************************************/
  997.  
  998. pascal    OSErr FSpGetDInfo(const FSSpec *spec,
  999.                           DInfo *fndrInfo)
  1000. {
  1001.     return ( GetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) );
  1002. }
  1003.  
  1004. /*****************************************************************************/
  1005.  
  1006. pascal    OSErr SetDInfo(short vRefNum,
  1007.                        long dirID,
  1008.                        ConstStr255Param name,
  1009.                        const DInfo *fndrInfo)
  1010. {
  1011.     CInfoPBRec pb;
  1012.     Str31 tempName;
  1013.     OSErr error;
  1014.  
  1015.     /* Protection against File Sharing problem */
  1016.     if ( (name == NULL) || (name[0] == 0) )
  1017.     {
  1018.         tempName[0] = 0;
  1019.         pb.dirInfo.ioNamePtr = tempName;
  1020.         pb.dirInfo.ioFDirIndex = -1;    /* use ioDirID */
  1021.     }
  1022.     else
  1023.     {
  1024.         pb.dirInfo.ioNamePtr = (StringPtr)name;
  1025.         pb.dirInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1026.     }
  1027.     pb.dirInfo.ioVRefNum = vRefNum;
  1028.     pb.dirInfo.ioDrDirID = dirID;
  1029.     error = PBGetCatInfoSync(&pb);
  1030.     if ( error == noErr )
  1031.     {
  1032.         if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  1033.         {
  1034.             /* it's a directory, set the DInfo */
  1035.             if ( pb.dirInfo.ioNamePtr == tempName )
  1036.                 pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
  1037.             else
  1038.                 pb.dirInfo.ioDrDirID = dirID;
  1039.             pb.dirInfo.ioDrUsrWds = *fndrInfo;
  1040.             error = PBSetCatInfoSync(&pb);
  1041.         }
  1042.         else
  1043.         {
  1044.             /* oops, a file was passed */
  1045.             error = dirNFErr;
  1046.         }
  1047.     }
  1048.     
  1049.     return ( error );
  1050. }
  1051.  
  1052. /*****************************************************************************/
  1053.  
  1054. pascal    OSErr FSpSetDInfo(const FSSpec *spec,
  1055.                           const DInfo *fndrInfo)
  1056. {
  1057.     return ( SetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) );
  1058. }
  1059.  
  1060. /*****************************************************************************/
  1061.  
  1062. pascal    OSErr    GetDirectoryID(short vRefNum,
  1063.                                long dirID,
  1064.                                ConstStr255Param name,
  1065.                                long *theDirID,
  1066.                                Boolean *isDirectory)
  1067. {
  1068.     CInfoPBRec pb;
  1069.     OSErr error;
  1070.  
  1071.     error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
  1072.     *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0;
  1073.     if ( *isDirectory )
  1074.     {
  1075.         *theDirID = pb.dirInfo.ioDrDirID;
  1076.     }
  1077.     else
  1078.     {
  1079.         *theDirID = pb.hFileInfo.ioFlParID;
  1080.     }
  1081.     
  1082.     return ( error );
  1083. }
  1084.  
  1085. /*****************************************************************************/
  1086.  
  1087. pascal    OSErr    FSpGetDirectoryID(const FSSpec *spec,
  1088.                                   long *theDirID,
  1089.                                   Boolean *isDirectory)
  1090. {
  1091.     return ( GetDirectoryID(spec->vRefNum, spec->parID, spec->name,
  1092.              theDirID, isDirectory) );
  1093. }
  1094.  
  1095. /*****************************************************************************/
  1096.  
  1097. pascal    OSErr    GetDirName(short vRefNum,
  1098.                            long dirID,
  1099.                            Str31 name)
  1100. {
  1101.     CInfoPBRec pb;
  1102.     OSErr error;
  1103.  
  1104.     if ( name != NULL )
  1105.     {
  1106.         pb.dirInfo.ioNamePtr = name;
  1107.         pb.dirInfo.ioVRefNum = vRefNum;
  1108.         pb.dirInfo.ioDrDirID = dirID;
  1109.         pb.dirInfo.ioFDirIndex = -1;    /* get information about ioDirID */
  1110.         error = PBGetCatInfoSync(&pb);
  1111.     }
  1112.     else
  1113.     {
  1114.         error = paramErr;
  1115.     }
  1116.     
  1117.     return ( error );
  1118. }
  1119.  
  1120. /*****************************************************************************/
  1121.  
  1122. pascal    OSErr    GetIOACUser(short vRefNum,
  1123.                             long dirID,
  1124.                             ConstStr255Param name,
  1125.                             SInt8 *ioACUser)
  1126. {
  1127.     CInfoPBRec pb;
  1128.     OSErr error;
  1129.     
  1130.     /* Clear ioACUser before calling PBGetCatInfo since some file systems
  1131.     ** don't bother to set or clear this field. If ioACUser isn't set by the
  1132.     ** file system, then you'll get the zero value back (full access) which
  1133.     ** is the access you have on volumes that don't support ioACUser.
  1134.     */
  1135.     pb.dirInfo.ioACUser = 0;    /* ioACUser used to be filler2 */
  1136.     error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
  1137.     if ( (error == noErr) && (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 )
  1138.     {
  1139.         /* oops, a file was passed */
  1140.         error = dirNFErr;
  1141.     }
  1142.     *ioACUser = pb.dirInfo.ioACUser;
  1143.     
  1144.     return ( error );
  1145. }
  1146.  
  1147. /*****************************************************************************/
  1148.  
  1149. pascal    OSErr    FSpGetIOACUser(const FSSpec *spec,
  1150.                                SInt8 *ioACUser)
  1151. {
  1152.     return ( GetIOACUser(spec->vRefNum, spec->parID, spec->name, ioACUser) );
  1153. }
  1154.  
  1155. /*****************************************************************************/
  1156.  
  1157. pascal    OSErr    GetParentID(short vRefNum,
  1158.                             long dirID,
  1159.                             ConstStr255Param name,
  1160.                             long *parID)
  1161. {
  1162.     CInfoPBRec pb;
  1163.     Str31 tempName;
  1164.     OSErr error;
  1165.     short realVRefNum;
  1166.     
  1167.     /* Protection against File Sharing problem */
  1168.     if ( (name == NULL) || (name[0] == 0) )
  1169.     {
  1170.         tempName[0] = 0;
  1171.         pb.hFileInfo.ioNamePtr = tempName;
  1172.         pb.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  1173.     }
  1174.     else
  1175.     {
  1176.         pb.hFileInfo.ioNamePtr = (StringPtr)name;
  1177.         pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1178.     }
  1179.     pb.hFileInfo.ioVRefNum = vRefNum;
  1180.     pb.hFileInfo.ioDirID = dirID;
  1181.     error = PBGetCatInfoSync(&pb);
  1182.     if ( error == noErr )
  1183.     {
  1184.         /*
  1185.         **    There's a bug in HFS where the wrong parent dir ID can be
  1186.         **    returned if multiple separators are used at the end of a
  1187.         **    pathname. For example, if the pathname:
  1188.         **        'volumeName:System Folder:Extensions::'
  1189.         **    is passed, the directory ID of the Extensions folder is
  1190.         **    returned in the ioFlParID field instead of fsRtDirID. Since
  1191.         **    multiple separators at the end of a pathname always specifies
  1192.         **    a directory, we only need to work-around cases where the
  1193.         **    object is a directory and there are multiple separators at
  1194.         **    the end of the name parameter.
  1195.         */
  1196.         if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  1197.         {
  1198.             /* Its a directory */
  1199.             
  1200.             /* is there a pathname? */
  1201.             if ( pb.hFileInfo.ioNamePtr == name )    
  1202.             {
  1203.                 /* could it contain multiple separators? */
  1204.                 if ( name[0] >= 2 )
  1205.                 {
  1206.                     /* does it contain multiple separators at the end? */
  1207.                     if ( (name[name[0]] == ':') && (name[name[0] - 1] == ':') )
  1208.                     {
  1209.                         /* OK, then do the extra stuff to get the correct parID */
  1210.                         
  1211.                         /* Get the real vRefNum (this should not fail) */
  1212.                         error = DetermineVRefNum(name, vRefNum, &realVRefNum);
  1213.                         if ( error == noErr )
  1214.                         {
  1215.                             /* we don't need the parent's name, but add protect against File Sharing problem */
  1216.                             tempName[0] = 0;
  1217.                             pb.dirInfo.ioNamePtr = tempName;
  1218.                             pb.dirInfo.ioVRefNum = realVRefNum;
  1219.                             /* pb.dirInfo.ioDrDirID already contains the */
  1220.                             /* dirID of the directory object */
  1221.                             pb.dirInfo.ioFDirIndex = -1;    /* get information about ioDirID */
  1222.                             error = PBGetCatInfoSync(&pb);
  1223.                             /* now, pb.dirInfo.ioDrParID contains the correct parID */
  1224.                         }
  1225.                     }
  1226.                 }
  1227.             }
  1228.         }
  1229.         
  1230.         /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */
  1231.         /* contains the parent ID */
  1232.         *parID = pb.hFileInfo.ioFlParID;
  1233.     }
  1234.     
  1235.     return ( error );
  1236. }
  1237.  
  1238. /*****************************************************************************/
  1239.  
  1240. pascal    OSErr    GetFilenameFromPathname(ConstStr255Param pathname,
  1241.                                         Str255 filename)
  1242. {
  1243.     short    index;
  1244.     short    nameEnd;
  1245.     OSErr    error;
  1246.  
  1247.     /* default to no filename */
  1248.     filename[0] = 0;
  1249.  
  1250.     /* check for no pathname */
  1251.     if ( pathname != NULL )
  1252.     {
  1253.         /* get string length */
  1254.         index = pathname[0];
  1255.         
  1256.         /* check for empty string */
  1257.         if ( index != 0 )
  1258.         {
  1259.             /* skip over last trailing colon (if any) */
  1260.             if ( pathname[index] == ':' )
  1261.             {
  1262.                 --index;
  1263.             }
  1264.  
  1265.             /* save the end of the string */
  1266.             nameEnd = index;
  1267.  
  1268.             /* if pathname ends with multiple colons, then this pathname refers */
  1269.             /* to a directory, not a file */
  1270.             if ( pathname[index] != ':' )
  1271.             {
  1272.                 /* parse backwards until we find a colon or hit the beginning of the pathname */
  1273.                 while ( (index != 0) && (pathname[index] != ':') )
  1274.                 {
  1275.                     --index;
  1276.                 }
  1277.                 
  1278.                 /* if we parsed to the beginning of the pathname and the pathname ended */
  1279.                 /* with a colon, then pathname is a full pathname to a volume, not a file */
  1280.                 if ( (index != 0) || (pathname[pathname[0]] != ':') )
  1281.                 {
  1282.                     /* get the filename and return noErr */
  1283.                     filename[0] = (char)(nameEnd - index);
  1284.                     BlockMoveData(&pathname[index+1], &filename[1], nameEnd - index);
  1285.                     error = noErr;
  1286.                 }
  1287.                 else
  1288.                 {
  1289.                     /* pathname to a volume, not a file */
  1290.                     error = notAFileErr;
  1291.                 }
  1292.             }
  1293.             else
  1294.             {
  1295.                 /* directory, not a file */
  1296.                 error = notAFileErr;
  1297.             }
  1298.         }
  1299.         else
  1300.         {
  1301.             /* empty string isn't a file */
  1302.             error = notAFileErr;
  1303.         }
  1304.     }
  1305.     else
  1306.     {
  1307.         /* NULL pathname isn't a file */
  1308.         error = notAFileErr;
  1309.     }
  1310.     
  1311.     return ( error );
  1312. }
  1313.  
  1314. /*****************************************************************************/
  1315.  
  1316. pascal    OSErr    GetObjectLocation(short vRefNum,
  1317.                                   long dirID,
  1318.                                   ConstStr255Param pathname,
  1319.                                   short *realVRefNum,
  1320.                                   long *realParID,
  1321.                                   Str255 realName,
  1322.                                   Boolean *isDirectory)
  1323. {
  1324.     OSErr error;
  1325.     CInfoPBRec pb;
  1326.     Str255 tempPathname;
  1327.     
  1328.     /* clear results */
  1329.     *realVRefNum = 0;
  1330.     *realParID = 0;
  1331.     realName[0] = 0;
  1332.     
  1333.     /*
  1334.     **    Get the real vRefNum
  1335.     */
  1336.     error = DetermineVRefNum(pathname, vRefNum, realVRefNum);
  1337.     if ( error == noErr )
  1338.     {
  1339.         /*
  1340.         **    Determine if the object already exists and if so,
  1341.         **    get the real parent directory ID if it's a file
  1342.         */
  1343.         
  1344.         /* Protection against File Sharing problem */
  1345.         if ( (pathname == NULL) || (pathname[0] == 0) )
  1346.         {
  1347.             tempPathname[0] = 0;
  1348.             pb.hFileInfo.ioNamePtr = tempPathname;
  1349.             pb.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  1350.         }
  1351.         else
  1352.         {
  1353.             pb.hFileInfo.ioNamePtr = (StringPtr)pathname;
  1354.             pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1355.         }
  1356.         pb.hFileInfo.ioVRefNum = vRefNum;
  1357.         pb.hFileInfo.ioDirID = dirID;
  1358.         error = PBGetCatInfoSync(&pb);
  1359.         if ( error == noErr )
  1360.         {
  1361.             /*
  1362.             **    The file system object is present and we have the file's real parID
  1363.             */
  1364.             
  1365.             /*    Is it a directory or a file? */
  1366.             *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0;
  1367.             if ( *isDirectory )
  1368.             {
  1369.                 /*
  1370.                 **    It's a directory, get its name and parent dirID, and then we're done
  1371.                 */
  1372.                 
  1373.                 pb.dirInfo.ioNamePtr = realName;
  1374.                 pb.dirInfo.ioVRefNum = *realVRefNum;
  1375.                 /* pb.dirInfo.ioDrDirID already contains the dirID of the directory object */
  1376.                 pb.dirInfo.ioFDirIndex = -1;    /* get information about ioDirID */
  1377.                 error = PBGetCatInfoSync(&pb);
  1378.                 
  1379.                 /* get the parent ID here, because the file system can return the */
  1380.                 /* wrong parent ID from the last call. */
  1381.                 *realParID = pb.dirInfo.ioDrParID;
  1382.             }
  1383.             else
  1384.             {
  1385.                 /*
  1386.                 **    It's a file - use the parent directory ID from the last call
  1387.                 **    to GetCatInfoparse, get the file name, and then we're done
  1388.                 */
  1389.                 *realParID = pb.hFileInfo.ioFlParID;    
  1390.                 error = GetFilenameFromPathname(pathname, realName);
  1391.             }
  1392.         }
  1393.         else if ( error == fnfErr )
  1394.         {
  1395.             /*
  1396.             **    The file system object is not present - see if its parent is present
  1397.             */
  1398.             
  1399.             /*
  1400.             **    Parse to get the object name from end of pathname
  1401.             */
  1402.             error = GetFilenameFromPathname(pathname, realName);
  1403.             
  1404.             /* if we can't get the object name from the end, we can't continue */
  1405.             if ( error == noErr )
  1406.             {
  1407.                 /*
  1408.                 **    What we want now is the pathname minus the object name
  1409.                 **    for example:
  1410.                 **    if pathname is 'vol:dir:file' tempPathname becomes 'vol:dir:'
  1411.                 **    if pathname is 'vol:dir:file:' tempPathname becomes 'vol:dir:'
  1412.                 **    if pathname is ':dir:file' tempPathname becomes ':dir:'
  1413.                 **    if pathname is ':dir:file:' tempPathname becomes ':dir:'
  1414.                 **    if pathname is ':file' tempPathname becomes ':'
  1415.                 **    if pathname is 'file or file:' tempPathname becomes ''
  1416.                 */
  1417.                 
  1418.                 /* get a copy of the pathname */
  1419.                 BlockMoveData(pathname, tempPathname, pathname[0] + 1);
  1420.                 
  1421.                 /* remove the object name */
  1422.                 tempPathname[0] -= realName[0];
  1423.                 /* and the trailing colon (if any) */
  1424.                 if ( pathname[pathname[0]] == ':' )
  1425.                 {
  1426.                     --tempPathname[0];
  1427.                 }
  1428.                 
  1429.                 /* OK, now get the parent's directory ID */
  1430.                 
  1431.                 /* Protection against File Sharing problem */
  1432.                 pb.hFileInfo.ioNamePtr = (StringPtr)tempPathname;
  1433.                 if ( tempPathname[0] != 0 )
  1434.                 {
  1435.                     pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1436.                 }
  1437.                 else
  1438.                 {
  1439.                     pb.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  1440.                 }
  1441.                 pb.hFileInfo.ioVRefNum = vRefNum;
  1442.                 pb.hFileInfo.ioDirID = dirID;
  1443.                 error = PBGetCatInfoSync(&pb);
  1444.                 *realParID = pb.dirInfo.ioDrDirID;
  1445.  
  1446.                 *isDirectory = false;    /* we don't know what the object is really going to be */
  1447.             }
  1448.             
  1449.             if ( error != noErr )
  1450.             {
  1451.                 error = dirNFErr;    /* couldn't find parent directory */
  1452.             }
  1453.             else
  1454.             {
  1455.                 error = fnfErr;    /* we found the parent, but not the file */
  1456.             }
  1457.         }
  1458.     }
  1459.     
  1460.     return ( error );
  1461. }
  1462.  
  1463. /*****************************************************************************/
  1464.  
  1465. pascal    OSErr    GetDirItems(short vRefNum,
  1466.                             long dirID,
  1467.                             ConstStr255Param name,
  1468.                             Boolean getFiles,
  1469.                             Boolean getDirectories,
  1470.                             FSSpecPtr items,
  1471.                             short reqItemCount,
  1472.                             short *actItemCount,
  1473.                             short *itemIndex) /* start with 1, then use what's returned */
  1474. {
  1475.     CInfoPBRec pb;
  1476.     OSErr error;
  1477.     long theDirID;
  1478.     Boolean isDirectory;
  1479.     FSSpec *endItemsArray;
  1480.     
  1481.     if ( *itemIndex > 0 )
  1482.     {
  1483.         /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */
  1484.         /* to this routine, I could rip out calls to DetermineVRefNum and GetDirectoryID and this */
  1485.         /* routine would be much faster because of the overhead of DetermineVRefNum and */
  1486.         /* GetDirectoryID and because GetDirectoryID blows away the directory index hint the Macintosh */
  1487.         /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */
  1488.         /* pass a big array of FSSpecs so you can get the directory's contents with few calls */
  1489.         /* to this routine. */
  1490.         
  1491.         /* get the real volume reference number */
  1492.         error = DetermineVRefNum(name, vRefNum, &pb.hFileInfo.ioVRefNum);
  1493.         if ( error == noErr )
  1494.         {
  1495.             /* and the real directory ID of this directory (and make sure it IS a directory) */
  1496.             error = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory);
  1497.             if ( error == noErr )
  1498.             {
  1499.                 if ( isDirectory )
  1500.                 {
  1501.                     *actItemCount = 0;
  1502.                     endItemsArray = items + reqItemCount;
  1503.                     while ( (items < endItemsArray) && (error == noErr) )
  1504.                     {
  1505.                         pb.hFileInfo.ioNamePtr = (StringPtr) &items->name;
  1506.                         pb.hFileInfo.ioDirID = theDirID;
  1507.                         pb.hFileInfo.ioFDirIndex = *itemIndex;
  1508.                         error = PBGetCatInfoSync(&pb);
  1509.                         if ( error == noErr )
  1510.                         {
  1511.                             items->parID = pb.hFileInfo.ioFlParID;    /* return item's parID */
  1512.                             items->vRefNum = pb.hFileInfo.ioVRefNum;    /* return item's vRefNum */
  1513.                             ++*itemIndex;    /* prepare to get next item in directory */
  1514.                             
  1515.                             if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  1516.                             {
  1517.                                 if ( getDirectories )
  1518.                                 {
  1519.                                     ++*actItemCount; /* keep this item */
  1520.                                     ++items; /* point to next item */
  1521.                                 }
  1522.                             }
  1523.                             else
  1524.                             {
  1525.                                 if ( getFiles )
  1526.                                 {
  1527.                                     ++*actItemCount; /* keep this item */
  1528.                                     ++items; /* point to next item */
  1529.                                 }
  1530.                             }
  1531.                         }
  1532.                     }
  1533.                 }
  1534.                 else
  1535.                 {
  1536.                     /* it wasn't a directory */
  1537.                     error = dirNFErr;
  1538.                 }
  1539.             }
  1540.         }
  1541.     }
  1542.     else
  1543.     {
  1544.         /* bad itemIndex */
  1545.         error = paramErr;
  1546.     }
  1547.     
  1548.     return ( error );
  1549. }
  1550.  
  1551. /*****************************************************************************/
  1552.  
  1553. static    void    DeleteLevel(long dirToDelete,
  1554.                             DeleteEnumGlobalsPtr theGlobals)
  1555. {
  1556.     long savedDir;
  1557.     
  1558.     do
  1559.     {
  1560.         /* prepare to delete directory */
  1561.         theGlobals->myPB.ciPB.dirInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
  1562.         theGlobals->myPB.ciPB.dirInfo.ioFDirIndex = 1;    /* get first item */
  1563.         theGlobals->myPB.ciPB.dirInfo.ioDrDirID = dirToDelete;    /* in this directory */
  1564.         theGlobals->error = PBGetCatInfoSync(&(theGlobals->myPB.ciPB));
  1565.         if ( theGlobals->error == noErr )
  1566.         {
  1567.             savedDir = dirToDelete;
  1568.             /* We have an item.  Is it a file or directory? */
  1569.             if ( (theGlobals->myPB.ciPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  1570.             {
  1571.                 /* it's a directory */
  1572.                 savedDir = theGlobals->myPB.ciPB.dirInfo.ioDrDirID;    /* save dirID of directory instead */
  1573.                 DeleteLevel(theGlobals->myPB.ciPB.dirInfo.ioDrDirID, theGlobals);    /* Delete its contents */
  1574.                 theGlobals->myPB.ciPB.dirInfo.ioNamePtr = NULL;    /* prepare to delete directory */
  1575.             }
  1576.             if ( theGlobals->error == noErr )
  1577.             {
  1578.                 theGlobals->myPB.ciPB.dirInfo.ioDrDirID = savedDir;    /* restore dirID */
  1579.                 theGlobals->myPB.hPB.fileParam.ioFVersNum = 0;    /* just in case it's used on an MFS volume... */
  1580.                 theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB));    /* delete this item */
  1581.                 if ( theGlobals->error == fLckdErr )
  1582.                 {
  1583.                     (void) PBHRstFLockSync(&(theGlobals->myPB.hPB));    /* unlock it */
  1584.                     theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB));    /* and try again */
  1585.                 }
  1586.             }
  1587.         }
  1588.     } while ( theGlobals->error == noErr );
  1589.     
  1590.     if ( theGlobals->error == fnfErr )
  1591.     {
  1592.         theGlobals->error = noErr;
  1593.     }
  1594. }
  1595.  
  1596. /*****************************************************************************/
  1597.  
  1598. pascal    OSErr    DeleteDirectoryContents(short vRefNum,
  1599.                                          long dirID,
  1600.                                         ConstStr255Param name)
  1601. {
  1602.     DeleteEnumGlobals theGlobals;
  1603.     Boolean    isDirectory;
  1604.     OSErr error;
  1605.  
  1606.     /*  Get the real dirID and make sure it is a directory. */
  1607.     error = GetDirectoryID(vRefNum, dirID, name, &dirID, &isDirectory);
  1608.     if ( error == noErr )
  1609.     {
  1610.         if ( isDirectory )
  1611.         {
  1612.             /* Get the real vRefNum */
  1613.             error = DetermineVRefNum(name, vRefNum, &vRefNum);
  1614.             if ( error == noErr )
  1615.             {
  1616.                 /* Set up the globals we need to access from the recursive routine. */
  1617.                 theGlobals.myPB.ciPB.dirInfo.ioVRefNum = vRefNum;
  1618.                     
  1619.                 /* Here we go into recursion land... */
  1620.                 DeleteLevel(dirID, &theGlobals);
  1621.                 error = theGlobals.error;
  1622.             }
  1623.         }
  1624.         else
  1625.         {
  1626.             error = dirNFErr;
  1627.         }
  1628.     }
  1629.     
  1630.     return ( error );
  1631. }
  1632.  
  1633. /*****************************************************************************/
  1634.  
  1635. pascal    OSErr    DeleteDirectory(short vRefNum,
  1636.                                 long dirID,
  1637.                                 ConstStr255Param name)
  1638. {
  1639.     OSErr error;
  1640.     
  1641.     /* Make sure a directory was specified and then delete its contents */
  1642.     error = DeleteDirectoryContents(vRefNum, dirID, name);
  1643.     if ( error == noErr )
  1644.     {
  1645.         error = HDelete(vRefNum, dirID, name);
  1646.         if ( error == fLckdErr )
  1647.         {
  1648.             (void) HRstFLock(vRefNum, dirID, name);    /* unlock the directory locked by AppleShare */
  1649.             error = HDelete(vRefNum, dirID, name);    /* and try again */
  1650.         }
  1651.     }
  1652.     
  1653.     return ( error );
  1654. }
  1655.  
  1656. /*****************************************************************************/
  1657.  
  1658. pascal    OSErr    CheckObjectLock(short vRefNum,
  1659.                                 long dirID,
  1660.                                 ConstStr255Param name)
  1661. {
  1662.     CInfoPBRec pb;
  1663.     OSErr error;
  1664.     
  1665.     error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
  1666.     if ( error == noErr )
  1667.     {
  1668.         /* check locked bit */
  1669.         if ( (pb.hFileInfo.ioFlAttrib & 0x01) != 0 )
  1670.         {
  1671.             error = fLckdErr;
  1672.         }
  1673.     }
  1674.     
  1675.     return ( error );
  1676. }
  1677.  
  1678. /*****************************************************************************/
  1679.  
  1680. pascal    OSErr    FSpCheckObjectLock(const FSSpec *spec)
  1681. {
  1682.     return ( CheckObjectLock(spec->vRefNum, spec->parID, spec->name) );
  1683. }
  1684.  
  1685. /*****************************************************************************/
  1686.  
  1687. pascal    OSErr    GetFileSize(short vRefNum,
  1688.                             long dirID,
  1689.                             ConstStr255Param fileName,
  1690.                             long *dataSize,
  1691.                             long *rsrcSize)
  1692. {
  1693.     HParamBlockRec pb;
  1694.     OSErr error;
  1695.     
  1696.     pb.fileParam.ioNamePtr = (StringPtr)fileName;
  1697.     pb.fileParam.ioVRefNum = vRefNum;
  1698.     pb.fileParam.ioFVersNum = 0;
  1699.     pb.fileParam.ioDirID = dirID;
  1700.     pb.fileParam.ioFDirIndex = 0;
  1701.     error = PBHGetFInfoSync(&pb);
  1702.     if ( error == noErr )
  1703.     {
  1704.         *dataSize = pb.fileParam.ioFlLgLen;
  1705.         *rsrcSize = pb.fileParam.ioFlRLgLen;
  1706.     }
  1707.     
  1708.     return ( error );
  1709. }
  1710.  
  1711. /*****************************************************************************/
  1712.  
  1713. pascal    OSErr    FSpGetFileSize(const FSSpec *spec,
  1714.                                long *dataSize,
  1715.                                long *rsrcSize)
  1716. {
  1717.     return ( GetFileSize(spec->vRefNum, spec->parID, spec->name, dataSize, rsrcSize) );
  1718. }
  1719.  
  1720. /*****************************************************************************/
  1721.  
  1722. pascal    OSErr    BumpDate(short vRefNum,
  1723.                          long dirID,
  1724.                          ConstStr255Param name)
  1725. /* Given a file or directory, change its modification date to the current date/time. */
  1726. {
  1727.     CInfoPBRec pb;
  1728.     Str31 tempName;
  1729.     OSErr error;
  1730.     unsigned long secs;
  1731.  
  1732.     /* Protection against File Sharing problem */
  1733.     if ( (name == NULL) || (name[0] == 0) )
  1734.     {
  1735.         tempName[0] = 0;
  1736.         pb.hFileInfo.ioNamePtr = tempName;
  1737.         pb.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  1738.     }
  1739.     else
  1740.     {
  1741.         pb.hFileInfo.ioNamePtr = (StringPtr)name;
  1742.         pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1743.     }
  1744.     pb.hFileInfo.ioVRefNum = vRefNum;
  1745.     pb.hFileInfo.ioDirID = dirID;
  1746.     error = PBGetCatInfoSync(&pb);
  1747.     if ( error == noErr )
  1748.     {
  1749.         GetDateTime(&secs);
  1750.         /* set mod date to current date, or one second into the future
  1751.             if mod date = current date */
  1752.         pb.hFileInfo.ioFlMdDat = (secs == pb.hFileInfo.ioFlMdDat) ? (++secs) : (secs);
  1753.         if ( pb.dirInfo.ioNamePtr == tempName )
  1754.         {
  1755.             pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID;
  1756.         }
  1757.         else
  1758.         {
  1759.             pb.hFileInfo.ioDirID = dirID;
  1760.         }
  1761.         error = PBSetCatInfoSync(&pb);
  1762.     }
  1763.     
  1764.     return ( error );
  1765. }
  1766.  
  1767. /*****************************************************************************/
  1768.  
  1769. pascal    OSErr    FSpBumpDate(const FSSpec *spec)
  1770. {
  1771.     return ( BumpDate(spec->vRefNum, spec->parID, spec->name) );
  1772. }
  1773.  
  1774. /*****************************************************************************/
  1775.  
  1776. pascal    OSErr    ChangeCreatorType(short vRefNum,
  1777.                                   long dirID,
  1778.                                   ConstStr255Param name,
  1779.                                   OSType creator,
  1780.                                   OSType fileType)
  1781. {
  1782.     CInfoPBRec pb;
  1783.     OSErr error;
  1784.     short realVRefNum;
  1785.     long parID;
  1786.  
  1787.     pb.hFileInfo.ioNamePtr = (StringPtr)name;
  1788.     pb.hFileInfo.ioVRefNum = vRefNum;
  1789.     pb.hFileInfo.ioDirID = dirID;
  1790.     pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1791.     error = PBGetCatInfoSync(&pb);
  1792.     if ( error == noErr )
  1793.     {
  1794.         if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 )    /* if file */
  1795.         {
  1796.             parID = pb.hFileInfo.ioFlParID;    /* save parent dirID for BumpDate call */
  1797.  
  1798.             /* If creator not 0x00000000, change creator */
  1799.             if ( creator != (OSType)0x00000000 )
  1800.             {
  1801.                 pb.hFileInfo.ioFlFndrInfo.fdCreator = creator;
  1802.             }
  1803.             
  1804.             /* If fileType not 0x00000000, change fileType */
  1805.             if ( fileType != (OSType)0x00000000 )
  1806.             {
  1807.                 pb.hFileInfo.ioFlFndrInfo.fdType = fileType;
  1808.             }
  1809.                 
  1810.             pb.hFileInfo.ioDirID = dirID;
  1811.             error = PBSetCatInfoSync(&pb);    /* now, save the new information back to disk */
  1812.  
  1813.             if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */
  1814.             {
  1815.                 /* get the real vRefNum in case a full pathname was passed */
  1816.                 error = DetermineVRefNum(name, vRefNum, &realVRefNum);
  1817.                 if ( error == noErr )
  1818.                 {
  1819.                     error = BumpDate(realVRefNum, parID, NULL);
  1820.                         /* and bump the parent directory's mod date to wake up the Finder */
  1821.                         /* to the change we just made */
  1822.                 }
  1823.             }
  1824.         }
  1825.         else
  1826.         {
  1827.             /* it was a directory, not a file */
  1828.             error = notAFileErr;
  1829.         }
  1830.     }
  1831.     
  1832.     return ( error );
  1833. }
  1834.  
  1835. /*****************************************************************************/
  1836.  
  1837. pascal    OSErr    FSpChangeCreatorType(const FSSpec *spec,
  1838.                                      OSType creator,
  1839.                                      OSType fileType)
  1840. {
  1841.     return ( ChangeCreatorType(spec->vRefNum, spec->parID, spec->name, creator, fileType) );
  1842. }
  1843.  
  1844. /*****************************************************************************/
  1845.  
  1846. pascal    OSErr    ChangeFDFlags(short vRefNum,
  1847.                               long dirID,
  1848.                               ConstStr255Param name,
  1849.                               Boolean    setBits,
  1850.                               unsigned short flagBits)
  1851. {
  1852.     CInfoPBRec pb;
  1853.     Str31 tempName;
  1854.     OSErr error;
  1855.     short realVRefNum;
  1856.     long parID;
  1857.  
  1858.     /* Protection against File Sharing problem */
  1859.     if ( (name == NULL) || (name[0] == 0) )
  1860.     {
  1861.         tempName[0] = 0;
  1862.         pb.hFileInfo.ioNamePtr = tempName;
  1863.         pb.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  1864.     }
  1865.     else
  1866.     {
  1867.         pb.hFileInfo.ioNamePtr = (StringPtr)name;
  1868.         pb.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  1869.     }
  1870.     pb.hFileInfo.ioVRefNum = vRefNum;
  1871.     pb.hFileInfo.ioDirID = dirID;
  1872.     error = PBGetCatInfoSync(&pb);
  1873.     if ( error == noErr )
  1874.     {
  1875.         parID = pb.hFileInfo.ioFlParID;    /* save parent dirID for BumpDate call */
  1876.         
  1877.         /* set or clear the appropriate bits in the Finder flags */
  1878.         if ( setBits )
  1879.         {
  1880.             /* OR in the bits */
  1881.             pb.hFileInfo.ioFlFndrInfo.fdFlags |= flagBits;
  1882.         }
  1883.         else
  1884.         {
  1885.             /* AND out the bits */
  1886.             pb.hFileInfo.ioFlFndrInfo.fdFlags &= ~flagBits;
  1887.         }
  1888.             
  1889.         if ( pb.dirInfo.ioNamePtr == tempName )
  1890.         {
  1891.             pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID;
  1892.         }
  1893.         else
  1894.         {
  1895.             pb.hFileInfo.ioDirID = dirID;
  1896.         }
  1897.         
  1898.         error = PBSetCatInfoSync(&pb);    /* now, save the new information back to disk */
  1899.  
  1900.         if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */
  1901.         {
  1902.             /* get the real vRefNum in case a full pathname was passed */
  1903.             error = DetermineVRefNum(name, vRefNum, &realVRefNum);
  1904.             if ( error == noErr )
  1905.             {
  1906.                 error = BumpDate(realVRefNum, parID, NULL);
  1907.                     /* and bump the parent directory's mod date to wake up the Finder */
  1908.                     /* to the change we just made */
  1909.             }
  1910.         }
  1911.     }
  1912.     
  1913.     return ( error );
  1914. }
  1915.  
  1916. /*****************************************************************************/
  1917.  
  1918. pascal    OSErr    FSpChangeFDFlags(const FSSpec *spec,
  1919.                                  Boolean setBits,
  1920.                                  unsigned short flagBits)
  1921. {
  1922.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, setBits, flagBits) );
  1923. }
  1924.  
  1925. /*****************************************************************************/
  1926.  
  1927. pascal    OSErr    SetIsInvisible(short vRefNum,
  1928.                                long dirID,
  1929.                                ConstStr255Param name)
  1930.     /* Given a file or directory, make it invisible. */
  1931. {
  1932.     return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsInvisible) );
  1933. }
  1934.  
  1935. /*****************************************************************************/
  1936.  
  1937. pascal    OSErr    FSpSetIsInvisible(const FSSpec *spec)
  1938.     /* Given a file or directory, make it invisible. */
  1939. {
  1940.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsInvisible) );
  1941. }
  1942.  
  1943. /*****************************************************************************/
  1944.  
  1945. pascal    OSErr    ClearIsInvisible(short vRefNum,
  1946.                                  long dirID,
  1947.                                  ConstStr255Param name)
  1948.     /* Given a file or directory, make it visible. */
  1949. {
  1950.     return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsInvisible) );
  1951. }
  1952.  
  1953. /*****************************************************************************/
  1954.  
  1955. pascal    OSErr    FSpClearIsInvisible(const FSSpec *spec)
  1956.     /* Given a file or directory, make it visible. */
  1957. {
  1958.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsInvisible) );
  1959. }
  1960.  
  1961. /*****************************************************************************/
  1962.  
  1963. pascal    OSErr    SetNameLocked(short vRefNum,
  1964.                               long dirID,
  1965.                               ConstStr255Param name)
  1966.     /* Given a file or directory, lock its name. */
  1967. {
  1968.     return ( ChangeFDFlags(vRefNum, dirID, name, true, kNameLocked) );
  1969. }
  1970.  
  1971. /*****************************************************************************/
  1972.  
  1973. pascal    OSErr    FSpSetNameLocked(const FSSpec *spec)
  1974.     /* Given a file or directory, lock its name. */
  1975. {
  1976.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kNameLocked) );
  1977. }
  1978.  
  1979. /*****************************************************************************/
  1980.  
  1981. pascal    OSErr    ClearNameLocked(short vRefNum,
  1982.                                 long dirID,
  1983.                                 ConstStr255Param name)
  1984.     /* Given a file or directory, unlock its name. */
  1985. {
  1986.     return ( ChangeFDFlags(vRefNum, dirID, name, false, kNameLocked) );
  1987. }
  1988.  
  1989. /*****************************************************************************/
  1990.  
  1991. pascal    OSErr    FSpClearNameLocked(const FSSpec *spec)
  1992.     /* Given a file or directory, unlock its name. */
  1993. {
  1994.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kNameLocked) );
  1995. }
  1996.  
  1997. /*****************************************************************************/
  1998.  
  1999. pascal    OSErr    SetIsStationery(short vRefNum,
  2000.                                 long dirID,
  2001.                                 ConstStr255Param name)
  2002.     /* Given a file, make it a stationery pad. */
  2003. {
  2004.     return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsStationery) );
  2005. }
  2006.  
  2007. /*****************************************************************************/
  2008.  
  2009. pascal    OSErr    FSpSetIsStationery(const FSSpec *spec)
  2010.     /* Given a file, make it a stationery pad. */
  2011. {
  2012.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsStationery) );
  2013. }
  2014.  
  2015. /*****************************************************************************/
  2016.  
  2017. pascal    OSErr    ClearIsStationery(short vRefNum,
  2018.                                   long dirID,
  2019.                                   ConstStr255Param name)
  2020.     /* Given a file, clear the stationery bit. */
  2021. {
  2022.     return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsStationery) );
  2023. }
  2024.  
  2025. /*****************************************************************************/
  2026.  
  2027. pascal    OSErr    FSpClearIsStationery(const FSSpec *spec)
  2028.     /* Given a file, clear the stationery bit. */
  2029. {
  2030.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsStationery) );
  2031. }
  2032.  
  2033. /*****************************************************************************/
  2034.  
  2035. pascal    OSErr    SetHasCustomIcon(short vRefNum,
  2036.                                  long dirID,
  2037.                                  ConstStr255Param name)
  2038.     /* Given a file or directory, indicate that it has a custom icon. */
  2039. {
  2040.     return ( ChangeFDFlags(vRefNum, dirID, name, true, kHasCustomIcon) );
  2041. }
  2042.  
  2043. /*****************************************************************************/
  2044.  
  2045. pascal    OSErr    FSpSetHasCustomIcon(const FSSpec *spec)
  2046.     /* Given a file or directory, indicate that it has a custom icon. */
  2047. {
  2048.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kHasCustomIcon) );
  2049. }
  2050.  
  2051. /*****************************************************************************/
  2052.  
  2053. pascal    OSErr    ClearHasCustomIcon(short vRefNum,
  2054.                                    long dirID,
  2055.                                    ConstStr255Param name)
  2056.     /* Given a file or directory, indicate that it does not have a custom icon. */
  2057. {
  2058.     return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasCustomIcon) );
  2059. }
  2060.  
  2061. /*****************************************************************************/
  2062.  
  2063. pascal    OSErr    FSpClearHasCustomIcon(const FSSpec *spec)
  2064.     /* Given a file or directory, indicate that it does not have a custom icon. */
  2065. {
  2066.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasCustomIcon) );
  2067. }
  2068.  
  2069. /*****************************************************************************/
  2070.  
  2071. pascal    OSErr    ClearHasBeenInited(short vRefNum,
  2072.                                    long dirID,
  2073.                                    ConstStr255Param name)
  2074.     /* Given a file, clear its "has been inited" bit. */
  2075. {
  2076.     return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasBeenInited) );
  2077. }
  2078.  
  2079. /*****************************************************************************/
  2080.  
  2081. pascal    OSErr    FSpClearHasBeenInited(const FSSpec *spec)
  2082.     /* Given a file, clear its "has been inited" bit. */
  2083. {
  2084.     return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasBeenInited) );
  2085. }
  2086.  
  2087. /*****************************************************************************/
  2088.  
  2089. pascal    OSErr    CopyFileMgrAttributes(short srcVRefNum,
  2090.                                       long srcDirID,
  2091.                                       ConstStr255Param srcName,
  2092.                                       short dstVRefNum,
  2093.                                       long dstDirID,
  2094.                                       ConstStr255Param dstName,
  2095.                                       Boolean copyLockBit)
  2096. {
  2097.     UniversalFMPB pb;
  2098.     Str31 tempName;
  2099.     OSErr error;
  2100.     Boolean objectIsDirectory;
  2101.  
  2102.     pb.ciPB.hFileInfo.ioVRefNum = srcVRefNum;
  2103.     pb.ciPB.hFileInfo.ioDirID = srcDirID;
  2104.  
  2105.     /* Protection against File Sharing problem */
  2106.     if ( (srcName == NULL) || (srcName[0] == 0) )
  2107.     {
  2108.         tempName[0] = 0;
  2109.         pb.ciPB.hFileInfo.ioNamePtr = tempName;
  2110.         pb.ciPB.hFileInfo.ioFDirIndex = -1;    /* use ioDirID */
  2111.     }
  2112.     else
  2113.     {
  2114.         pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)srcName;
  2115.         pb.ciPB.hFileInfo.ioFDirIndex = 0;    /* use ioNamePtr and ioDirID */
  2116.     }
  2117.     error = PBGetCatInfoSync(&pb.ciPB);
  2118.     if ( error == noErr )
  2119.     {
  2120.         objectIsDirectory = ( (pb.ciPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 );
  2121.         pb.ciPB.hFileInfo.ioVRefNum = dstVRefNum;
  2122.         pb.ciPB.hFileInfo.ioDirID = dstDirID;
  2123.         if ( (dstName != NULL) && (dstName[0] == 0) )
  2124.         {
  2125.             pb.ciPB.hFileInfo.ioNamePtr = NULL;
  2126.         }
  2127.         else
  2128.         {
  2129.             pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)dstName;
  2130.         }
  2131.         /* don't copy the hasBeenInited bit */
  2132.         pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags = ( pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags & 0xfeff );
  2133.         error = PBSetCatInfoSync(&pb.ciPB);
  2134.         if ( (error == noErr) && (copyLockBit) && ((pb.ciPB.hFileInfo.ioFlAttrib & 0x01) != 0) )
  2135.         {
  2136.             pb.hPB.fileParam.ioFVersNum = 0;
  2137.             error = PBHSetFLockSync(&pb.hPB);
  2138.             if ( (error != noErr) && (objectIsDirectory) )
  2139.             {
  2140.                 error = noErr; /* ignore lock errors if destination is directory */
  2141.             }
  2142.         }
  2143.     }
  2144.     return ( error );
  2145. }
  2146.  
  2147. /*****************************************************************************/
  2148.  
  2149. pascal    OSErr    FSpCopyFileMgrAttributes(const FSSpec *srcSpec,
  2150.                                          const FSSpec *dstSpec,
  2151.                                          Boolean copyLockBit)
  2152. {
  2153.     return ( CopyFileMgrAttributes(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  2154.                                    dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
  2155.                                    copyLockBit) );
  2156. }
  2157.  
  2158. /*****************************************************************************/
  2159.  
  2160. pascal    OSErr    HOpenAware(short vRefNum,
  2161.                            long dirID,
  2162.                            ConstStr255Param fileName,
  2163.                            short denyModes,
  2164.                            short *refNum)
  2165. {
  2166.     HParamBlockRec pb;
  2167.     OSErr error;
  2168.     GetVolParmsInfoBuffer volParmsInfo;
  2169.     long infoSize = sizeof(GetVolParmsInfoBuffer);
  2170.  
  2171.     pb.ioParam.ioMisc = NULL;
  2172.     pb.fileParam.ioFVersNum = 0;
  2173.     pb.fileParam.ioNamePtr = (StringPtr)fileName;
  2174.     pb.fileParam.ioVRefNum = vRefNum;
  2175.     pb.fileParam.ioDirID = dirID;
  2176.  
  2177.     /* get volume attributes */
  2178.     /* this preflighting is needed because Foreign File Access based file systems don't */
  2179.     /* return the correct error result to the OpenDeny call */
  2180.     error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize);
  2181.     if ( (error == noErr) && hasOpenDeny(volParmsInfo) )
  2182.     {
  2183.         /* if volume supports OpenDeny, use it and return */
  2184.             pb.accessParam.ioDenyModes = denyModes;
  2185.             error = PBHOpenDenySync(&pb);
  2186.             *refNum = pb.ioParam.ioRefNum;
  2187.     }
  2188.     else if ( (error == noErr) || (error == paramErr) )    /* paramErr is OK, it just means this volume doesn't support GetVolParms */
  2189.     {
  2190.         /* OpenDeny isn't supported, so try File Manager Open functions */
  2191.         
  2192.         /* If request includes write permission, then see if the volume is */
  2193.         /* locked by hardware or software. The HFS file system doesn't check */
  2194.         /* for this when a file is opened - you only find out later when you */
  2195.         /* try to write and the write fails with a wPrErr or a vLckdErr. */
  2196.         
  2197.         if ( (denyModes & dmWr) != 0 )
  2198.         {
  2199.             error = CheckVolLock(fileName, vRefNum);
  2200.         }
  2201.         else
  2202.         {
  2203.             error = noErr;
  2204.         }
  2205.         
  2206.         if ( error == noErr )
  2207.         {
  2208.             /* Set File Manager permissions to closest thing possible */
  2209.             if ( (denyModes == dmWr) || (denyModes == dmRdWr) )
  2210.             {
  2211.                 pb.ioParam.ioPermssn = fsRdWrShPerm;
  2212.             }
  2213.             else
  2214.             {
  2215.                 pb.ioParam.ioPermssn = denyModes % 4;
  2216.             }
  2217.  
  2218.             error = PBHOpenDFSync(&pb);                /* Try OpenDF */
  2219.             if ( error == paramErr )
  2220.             {
  2221.                 error = PBHOpenSync(&pb);            /* OpenDF not supported, so try Open */
  2222.             }
  2223.             *refNum = pb.ioParam.ioRefNum;
  2224.         }
  2225.     }
  2226.     
  2227.     return ( error );
  2228. }
  2229.  
  2230. /*****************************************************************************/
  2231.  
  2232. pascal    OSErr    FSpOpenAware(const FSSpec *spec,
  2233.                              short denyModes,
  2234.                              short *refNum)
  2235. {
  2236.     return ( HOpenAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) );
  2237. }
  2238.  
  2239. /*****************************************************************************/
  2240.  
  2241. pascal    OSErr    HOpenRFAware(short vRefNum,
  2242.                              long dirID,
  2243.                              ConstStr255Param fileName,
  2244.                              short denyModes,
  2245.                              short *refNum)
  2246. {
  2247.     HParamBlockRec pb;
  2248.     OSErr error;
  2249.     GetVolParmsInfoBuffer volParmsInfo;
  2250.     long infoSize = sizeof(GetVolParmsInfoBuffer);
  2251.  
  2252.     pb.ioParam.ioMisc = NULL;
  2253.     pb.fileParam.ioFVersNum = 0;
  2254.     pb.fileParam.ioNamePtr = (StringPtr)fileName;
  2255.     pb.fileParam.ioVRefNum = vRefNum;
  2256.     pb.fileParam.ioDirID = dirID;
  2257.  
  2258.     /* get volume attributes */
  2259.     /* this preflighting is needed because Foreign File Access based file systems don't */
  2260.     /* return the correct error result to the OpenRFDeny call */
  2261.     error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize);
  2262.     if ( (error == noErr) && hasOpenDeny(volParmsInfo) )
  2263.     {
  2264.         /* if volume supports OpenRFDeny, use it and return */
  2265.         if ( hasOpenDeny(volParmsInfo) )
  2266.         {
  2267.             pb.accessParam.ioDenyModes = denyModes;
  2268.             error = PBHOpenRFDenySync(&pb);
  2269.             *refNum = pb.ioParam.ioRefNum;
  2270.         }
  2271.     }
  2272.     else if ( (error == noErr) || (error == paramErr) )    /* paramErr is OK, it just means this volume doesn't support GetVolParms */
  2273.     {
  2274.         /* OpenRFDeny isn't supported, so try File Manager OpenRF function */
  2275.         
  2276.         /* If request includes write permission, then see if the volume is */
  2277.         /* locked by hardware or software. The HFS file system doesn't check */
  2278.         /* for this when a file is opened - you only find out later when you */
  2279.         /* try to write and the write fails with a wPrErr or a vLckdErr. */
  2280.         
  2281.         if ( (denyModes & dmWr) != 0 )
  2282.         {
  2283.             error = CheckVolLock(fileName, vRefNum);
  2284.         }
  2285.         else
  2286.         {
  2287.             error = noErr;
  2288.         }
  2289.         
  2290.         if ( error == noErr )
  2291.         {
  2292.             /* Set File Manager permissions to closest thing possible */
  2293.             if ( (denyModes == dmWr) || (denyModes == dmRdWr) )
  2294.             {
  2295.                 pb.ioParam.ioPermssn = fsRdWrShPerm;
  2296.             }
  2297.             else
  2298.             {
  2299.                 pb.ioParam.ioPermssn = denyModes % 4;
  2300.             }
  2301.  
  2302.             error = PBHOpenRFSync(&pb);
  2303.             *refNum = pb.ioParam.ioRefNum;
  2304.         }
  2305.     }
  2306.  
  2307.     return ( error );
  2308. }
  2309.  
  2310. /*****************************************************************************/
  2311.  
  2312. pascal    OSErr    FSpOpenRFAware(const FSSpec *spec,
  2313.                                short denyModes,
  2314.                                short *refNum)
  2315. {
  2316.     return ( HOpenRFAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) );
  2317. }
  2318.  
  2319. /*****************************************************************************/
  2320.  
  2321. pascal    OSErr    FSReadNoCache(short refNum,
  2322.                               long *count,
  2323.                               void *buffPtr)
  2324. {
  2325.     ParamBlockRec pb;
  2326.     OSErr error;
  2327.  
  2328.     pb.ioParam.ioRefNum = refNum;
  2329.     pb.ioParam.ioBuffer = (Ptr)buffPtr;
  2330.     pb.ioParam.ioReqCount = *count;
  2331.     pb.ioParam.ioPosMode = fsAtMark + 0x0020;    /* fsAtMark + noCacheBit */
  2332.     pb.ioParam.ioPosOffset = 0;
  2333.     error = PBReadSync(&pb);
  2334.     *count = pb.ioParam.ioActCount;
  2335.     return ( error );
  2336. }
  2337.  
  2338. /*****************************************************************************/
  2339.  
  2340. pascal    OSErr    FSWriteNoCache(short refNum,
  2341.                                long *count,
  2342.                                const void *buffPtr)
  2343. {
  2344.     ParamBlockRec pb;
  2345.     OSErr error;
  2346.  
  2347.     pb.ioParam.ioRefNum = refNum;
  2348.     pb.ioParam.ioBuffer = (Ptr)buffPtr;
  2349.     pb.ioParam.ioReqCount = *count;
  2350.     pb.ioParam.ioPosMode = fsAtMark + 0x0020;    /* fsAtMark + noCacheBit */
  2351.     pb.ioParam.ioPosOffset = 0;
  2352.     error = PBWriteSync(&pb);
  2353.     *count = pb.ioParam.ioActCount;
  2354.     return ( error );
  2355. }
  2356.  
  2357. /*****************************************************************************/
  2358.  
  2359. /*
  2360. **    See if numBytes bytes of buffer1 are equal to buffer2.
  2361. */
  2362. static    Boolean EqualMemory(const void *buffer1, const void *buffer2, unsigned long numBytes)
  2363. {
  2364.     register unsigned char *b1 = (unsigned char *)buffer1;
  2365.     register unsigned char *b2 = (unsigned char *)buffer2;
  2366.  
  2367.     if ( b1 != b2 )    /* if buffer pointers are same, then they are equal */
  2368.     {
  2369.         while ( numBytes > 0 )
  2370.         {
  2371.             /* compare the bytes and then increment the pointers */
  2372.             if ( (*b1++ - *b2++) != 0 )
  2373.             {
  2374.                 return ( false );
  2375.             }
  2376.             --numBytes;
  2377.         }
  2378.     }
  2379.     
  2380.     return ( true );
  2381. }
  2382.  
  2383. /*****************************************************************************/
  2384.  
  2385. /*
  2386. **    Read any number of bytes from an open file using read-verify mode.
  2387. **    The FSReadVerify function reads any number of bytes from an open file
  2388. **    and verifies them against the data in the buffer pointed to by buffPtr.
  2389. **    
  2390. **    Because of a bug in the HFS file system, only non-block aligned parts of
  2391. **    the read are verified against the buffer data and the rest is *copied*
  2392. **    into the buffer.  Thus, you shouldn't verify against your original data;
  2393. **    instead, you should verify against a copy of the original data and then
  2394. **    compare the read-verified copy against the original data after calling
  2395. **    FSReadVerify. That's why this function isn't exported - it needs the
  2396. **    wrapper provided by FSWriteVerify.
  2397. */
  2398. static    OSErr    FSReadVerify(short refNum,
  2399.                              long *count,
  2400.                              void *buffPtr)
  2401. {
  2402.     ParamBlockRec    pb;
  2403.     OSErr            result;
  2404.  
  2405.     pb.ioParam.ioRefNum = refNum;
  2406.     pb.ioParam.ioBuffer = (Ptr)buffPtr;
  2407.     pb.ioParam.ioReqCount = *count;
  2408.     pb.ioParam.ioPosMode = fsAtMark + rdVerify;
  2409.     pb.ioParam.ioPosOffset = 0;
  2410.     result = PBReadSync(&pb);
  2411.     *count = pb.ioParam.ioActCount;
  2412.     return ( result );
  2413. }
  2414.  
  2415. /*****************************************************************************/
  2416.  
  2417. pascal    OSErr    FSWriteVerify(short refNum,
  2418.                               long *count,
  2419.                               const void *buffPtr)
  2420. {
  2421.     Ptr        verifyBuffer;
  2422.     long    position;
  2423.     long    bufferSize;
  2424.     long    byteCount;
  2425.     long    bytesVerified;
  2426.     Ptr        startVerify;
  2427.     OSErr    result;
  2428.     
  2429.     /*
  2430.     **    Allocate the verify buffer
  2431.     **    Try to get get a large enough buffer to verify in one pass.
  2432.     **    If that fails, use GetTempBuffer to get a buffer.
  2433.     */
  2434.     bufferSize = *count;
  2435.     verifyBuffer = NewPtr(bufferSize);
  2436.     if ( verifyBuffer == NULL )
  2437.     {
  2438.         verifyBuffer = GetTempBuffer(bufferSize, &bufferSize);
  2439.     }
  2440.     if ( verifyBuffer != NULL )
  2441.     {        
  2442.         /* Save the current position */
  2443.         result = GetFPos(refNum, &position);
  2444.         if ( result == noErr )
  2445.         {
  2446.             /* Write the data */
  2447.             result = FSWrite(refNum, count, buffPtr);
  2448.             if ( result == noErr )
  2449.             {
  2450.                 /* Restore the original position */
  2451.                 result = SetFPos(refNum, fsFromStart, position);
  2452.                 if ( result == noErr )
  2453.                 {
  2454.                     /*
  2455.                     **    *count            = total number of bytes to verify
  2456.                     **    bufferSize        = the size of the verify buffer
  2457.                     **    bytesVerified    = number of bytes verified
  2458.                     **    byteCount        = number of bytes to verify this pass
  2459.                     **    startVerify        = position in buffPtr
  2460.                     */
  2461.                     bytesVerified = 0;
  2462.                     startVerify = (Ptr)buffPtr;
  2463.                     while ( (bytesVerified < *count) && ( result == noErr ) )
  2464.                     {
  2465.                         if ( (*count - bytesVerified) > bufferSize )
  2466.                         {
  2467.                             byteCount = bufferSize;
  2468.                         }
  2469.                         else
  2470.                         {
  2471.                             byteCount = *count - bytesVerified;
  2472.                         }
  2473.                         /*
  2474.                         **    Copy the write buffer into the verify buffer.
  2475.                         **    This step is needed because the File Manager
  2476.                         **    compares the data in any non-block aligned
  2477.                         **    data at the beginning and end of the read-verify
  2478.                         **    request back into the file system's cache
  2479.                         **    to the data in verify Buffer. However, the
  2480.                         **    File Manager does not compare any full blocks
  2481.                         **    and instead copies them into the verify buffer
  2482.                         **    so we still have to compare the buffers again
  2483.                         **    after the read-verify request completes.
  2484.                         */
  2485.                         BlockMoveData(startVerify, verifyBuffer, byteCount);
  2486.                         
  2487.                         /* Read-verify the data back into the verify buffer */
  2488.                         result = FSReadVerify(refNum, &byteCount, verifyBuffer);
  2489.                         if ( result == noErr )
  2490.                         {
  2491.                             /* See if the buffers are the same */
  2492.                             if ( !EqualMemory(verifyBuffer, startVerify, byteCount) )
  2493.                             {
  2494.                                 result = ioErr;
  2495.                             }
  2496.                             startVerify += byteCount;
  2497.                             bytesVerified += byteCount;
  2498.                         }
  2499.                     }
  2500.                 }
  2501.             }
  2502.         }
  2503.         DisposePtr(verifyBuffer);
  2504.     }
  2505.     else
  2506.     {
  2507.         result = memFullErr;
  2508.     }
  2509.     return ( result );
  2510. }
  2511.  
  2512. /*****************************************************************************/
  2513.  
  2514. pascal    OSErr    CopyFork(short srcRefNum,
  2515.                          short dstRefNum,
  2516.                          void *copyBufferPtr,
  2517.                          long copyBufferSize)
  2518. {
  2519.     ParamBlockRec srcPB;
  2520.     ParamBlockRec dstPB;
  2521.     OSErr srcError;
  2522.     OSErr dstError;
  2523.  
  2524.     if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) )
  2525.         return ( paramErr );
  2526.     
  2527.     srcPB.ioParam.ioRefNum = srcRefNum;
  2528.     dstPB.ioParam.ioRefNum = dstRefNum;
  2529.  
  2530.     /* preallocate the destination fork and */
  2531.     /* ensure the destination fork's EOF is correct after the copy */
  2532.     srcError = PBGetEOFSync(&srcPB);
  2533.     if ( srcError != noErr )
  2534.         return ( srcError );
  2535.     dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc;
  2536.     dstError = PBSetEOFSync(&dstPB);
  2537.     if ( dstError != noErr )
  2538.         return ( dstError );
  2539.  
  2540.     /* reset source fork's mark */
  2541.     srcPB.ioParam.ioPosMode = fsFromStart;
  2542.     srcPB.ioParam.ioPosOffset = 0;
  2543.     srcError = PBSetFPosSync(&srcPB);
  2544.     if ( srcError != noErr )
  2545.         return ( srcError );
  2546.  
  2547.     /* reset destination fork's mark */
  2548.     dstPB.ioParam.ioPosMode = fsFromStart;
  2549.     dstPB.ioParam.ioPosOffset = 0;
  2550.     dstError = PBSetFPosSync(&dstPB);
  2551.     if ( dstError != noErr )
  2552.         return ( dstError );
  2553.  
  2554.     /* set up fields that won't change in the loop */
  2555.     srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  2556.     srcPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */
  2557.     /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */
  2558.     /* This will make writes on local volumes faster */
  2559.     if ( (copyBufferSize >= 512) && ((copyBufferSize & 0x1ff) != 0) )
  2560.     {
  2561.         srcPB.ioParam.ioReqCount = copyBufferSize & 0xfffffe00;
  2562.     }
  2563.     else
  2564.     {
  2565.         srcPB.ioParam.ioReqCount = copyBufferSize;
  2566.     }
  2567.     dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
  2568.     dstPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */
  2569.  
  2570.     while ( (srcError == noErr) && (dstError == noErr) )
  2571.     {
  2572.         srcError = PBReadSync(&srcPB);
  2573.         dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount;
  2574.         dstError = PBWriteSync(&dstPB);
  2575.     }
  2576.  
  2577.     /* make sure there were no errors at the destination */
  2578.     if ( dstError != noErr )
  2579.         return ( dstError );
  2580.  
  2581.     /* make sure the only error at the source was eofErr */
  2582.     if ( srcError != eofErr )
  2583.         return ( srcError );
  2584.  
  2585.     return ( noErr );
  2586. }
  2587.  
  2588. /*****************************************************************************/
  2589.  
  2590. pascal    OSErr    GetFileLocation(short refNum,
  2591.                                 short *vRefNum,
  2592.                                 long *dirID,
  2593.                                 StringPtr fileName)
  2594. {
  2595.     FCBPBRec pb;
  2596.     OSErr error;
  2597.  
  2598.     pb.ioNamePtr = fileName;
  2599.     pb.ioVRefNum = 0;
  2600.     pb.ioRefNum = refNum;
  2601.     pb.ioFCBIndx = 0;
  2602.     error = PBGetFCBInfoSync(&pb);
  2603.     *vRefNum = pb.ioFCBVRefNum;
  2604.     *dirID = pb.ioFCBParID;
  2605.     return ( error );
  2606. }
  2607.  
  2608. /*****************************************************************************/
  2609.  
  2610. pascal    OSErr    FSpGetFileLocation(short refNum,
  2611.                                    FSSpec *spec)
  2612. {
  2613.     return ( GetFileLocation(refNum, &(spec->vRefNum), &(spec->parID), spec->name) );
  2614. }
  2615.  
  2616. /*****************************************************************************/
  2617.  
  2618. pascal    OSErr    CopyDirectoryAccess(short srcVRefNum,
  2619.                                     long srcDirID,
  2620.                                     ConstStr255Param srcName,
  2621.                                     short dstVRefNum,
  2622.                                     long dstDirID,
  2623.                                     ConstStr255Param dstName)
  2624. {    
  2625.     OSErr error;
  2626.     GetVolParmsInfoBuffer infoBuffer;    /* Where PBGetVolParms dumps its info */
  2627.     long    dstServerAdr;                /* AppleTalk server address of destination (if any) */
  2628.     long    ownerID, groupID, accessRights;
  2629.     long    tempLong;
  2630.  
  2631.     /* See if destination supports directory access control */
  2632.     tempLong = sizeof(infoBuffer);
  2633.     error = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong);
  2634.     if ( (error == noErr) && hasAccessCntl(infoBuffer) )
  2635.     {
  2636.         if ( hasAccessCntl(infoBuffer) )
  2637.         {
  2638.             dstServerAdr = infoBuffer.vMServerAdr;
  2639.             
  2640.             /* See if source supports directory access control and is on same server */
  2641.             tempLong = sizeof(infoBuffer);
  2642.             error = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong);
  2643.             if ( error == noErr )
  2644.             {
  2645.                 if ( hasAccessCntl(infoBuffer) && (dstServerAdr == infoBuffer.vMServerAdr) )
  2646.                 {
  2647.                     /* both volumes support directory access control and they are */
  2648.                     /*  on same server, so copy the access information */
  2649.                     error = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights);
  2650.                     if ( error == noErr )
  2651.                     {
  2652.                         error = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights);
  2653.                     }
  2654.                 }
  2655.                 else
  2656.                 {
  2657.                     /* destination doesn't support directory access control or */
  2658.                     /* they volumes aren't on the same server */
  2659.                     error = paramErr;
  2660.                 }
  2661.             }
  2662.         }
  2663.         else
  2664.         {
  2665.             /* destination doesn't support directory access control */
  2666.             error = paramErr;
  2667.         }
  2668.     }
  2669.     
  2670.     return ( error );
  2671. }
  2672.  
  2673. /*****************************************************************************/
  2674.  
  2675. pascal    OSErr    FSpCopyDirectoryAccess(const FSSpec *srcSpec,
  2676.                                        const FSSpec *dstSpec)
  2677. {
  2678.     return ( CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  2679.                                 dstSpec->vRefNum, dstSpec->parID, dstSpec->name) );
  2680. }
  2681.  
  2682. /*****************************************************************************/
  2683.  
  2684. pascal    OSErr    HMoveRenameCompat(short vRefNum,
  2685.                                   long srcDirID,
  2686.                                   ConstStr255Param srcName,
  2687.                                   long dstDirID,
  2688.                                   ConstStr255Param dstpathName,
  2689.                                   ConstStr255Param copyName)
  2690. {
  2691.     OSErr                    error;
  2692.     GetVolParmsInfoBuffer    volParmsInfo;
  2693.     long                    infoSize;
  2694.     short                    realVRefNum;
  2695.     long                    realParID;
  2696.     Str31                    realName;
  2697.     Boolean                    isDirectory;
  2698.     long                    tempItemsDirID;
  2699.     long                    uniqueTempDirID;
  2700.     Str31                    uniqueTempDirName;
  2701.     unsigned short            uniqueNameoverflow;
  2702.     
  2703.     /* Get volume attributes */
  2704.     infoSize = sizeof(GetVolParmsInfoBuffer);
  2705.     error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize);
  2706.     if ( (error == noErr) && hasMoveRename(volParmsInfo) )
  2707.     {
  2708.         /* If volume supports move and rename, so use it and return */
  2709.         error = HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName);
  2710.     }
  2711.     else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
  2712.     {
  2713.         /* MoveRename isn't supported by this volume, so do it by hand */
  2714.         
  2715.         /* If copyName isn't supplied, we can simply CatMove and return */
  2716.         if ( copyName == NULL )
  2717.         {
  2718.             error = CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName);
  2719.         }
  2720.         else
  2721.         {
  2722.             /* Renaming is required, so we have some work to do... */
  2723.             
  2724.             /* Get the object's real name, real parent ID and real vRefNum */
  2725.             error = GetObjectLocation(vRefNum, srcDirID, (StringPtr)srcName,
  2726.                                         &realVRefNum, &realParID, realName, &isDirectory);
  2727.             if ( error == noErr )
  2728.             {
  2729.                 /* Find the Temporary Items Folder on that volume */
  2730.                 error = FindFolder(realVRefNum, kTemporaryFolderType, kCreateFolder,
  2731.                                     &realVRefNum, &tempItemsDirID);
  2732.                 if ( error == noErr )
  2733.                 {
  2734.                     /* Create a new uniquely named folder in the temporary items folder. */
  2735.                     /* This is done to avoid the case where 'realName' or 'copyName' already */
  2736.                     /* exists in the temporary items folder. */
  2737.                     
  2738.                     /* Start with current tick count as uniqueTempDirName */                    
  2739.                     NumToString(TickCount(), uniqueTempDirName);
  2740.                     uniqueNameoverflow = 0;
  2741.                     do
  2742.                     {
  2743.                         error = DirCreate(realVRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID);
  2744.                         if ( error == dupFNErr )
  2745.                         {
  2746.                             /* Duplicate name - change the first character to the next ASCII character */
  2747.                             ++uniqueTempDirName[1];
  2748.                             /* Make sure it isn't a colon! */
  2749.                             if ( uniqueTempDirName[1] == ':' )
  2750.                             {
  2751.                                 ++uniqueTempDirName[1];
  2752.                             }
  2753.                             /* Don't go too far... */
  2754.                             ++uniqueNameoverflow;
  2755.                         }
  2756.                     } while ( (error == dupFNErr) && (uniqueNameoverflow <= 64) ); /* 64 new files per 1/60th second - not likely! */
  2757.                     if ( error == noErr )
  2758.                     {
  2759.                         /* Move the object to the folder with uniqueTempDirID for renaming */
  2760.                         error = CatMove(realVRefNum, realParID, realName, uniqueTempDirID, NULL);
  2761.                         if ( error == noErr )
  2762.                         {
  2763.                             /* Rename the object */    
  2764.                             error = HRename(realVRefNum, uniqueTempDirID, realName, copyName);
  2765.                             if ( error == noErr )
  2766.                             {
  2767.                                 /* Move object to its new home */
  2768.                                 error = CatMove(realVRefNum, uniqueTempDirID, copyName, dstDirID, dstpathName);
  2769.                                 if ( error != noErr )
  2770.                                 {
  2771.                                     /* Error handling: rename object back to original name - ignore errors */
  2772.                                     (void) HRename(realVRefNum, uniqueTempDirID, copyName, realName);
  2773.                                 }
  2774.                             }
  2775.                             if ( error != noErr )
  2776.                             {
  2777.                                 /* Error handling: move object back to original location - ignore errors */
  2778.                                 (void) CatMove(realVRefNum, uniqueTempDirID, realName, realParID, NULL);
  2779.                             }
  2780.                         }
  2781.                         /* Done with ourTempDir, so delete it - ignore errors */
  2782.                         (void) HDelete(realVRefNum, uniqueTempDirID, NULL);
  2783.                     }
  2784.                 }
  2785.             }
  2786.         }
  2787.     }
  2788.     
  2789.     return ( error );
  2790. }
  2791.  
  2792. /*****************************************************************************/
  2793.  
  2794. pascal    OSErr    FSpMoveRenameCompat(const FSSpec *srcSpec,
  2795.                                     const FSSpec *dstSpec,
  2796.                                     ConstStr255Param copyName)
  2797. {
  2798.     /* make sure the FSSpecs refer to the same volume */
  2799.     if (srcSpec->vRefNum != dstSpec->vRefNum)
  2800.         return (diffVolErr);
  2801.     return ( HMoveRenameCompat(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  2802.                       dstSpec->parID, dstSpec->name, copyName) );
  2803. }
  2804.  
  2805. /*****************************************************************************/
  2806.  
  2807. pascal    void    BuildAFPVolMountInfo(short theFlags,
  2808.                                      char theNBPInterval,
  2809.                                      char theNBPCount,
  2810.                                      short theUAMType,
  2811.                                      Str31 theZoneName,
  2812.                                      Str31 theServerName,
  2813.                                      Str27 theVolName,
  2814.                                      Str31 theUserName,
  2815.                                      Str8 theUserPassWord,
  2816.                                      Str8 theVolPassWord,
  2817.                                      MyAFPVolMountInfo *theAFPInfo)
  2818. {
  2819.     /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */
  2820.     theAFPInfo->length = sizeof(MyAFPVolMountInfo);
  2821.     theAFPInfo->media = AppleShareMediaType;
  2822.     theAFPInfo->flags = theFlags;
  2823.     theAFPInfo->nbpInterval = theNBPInterval;
  2824.     theAFPInfo->nbpCount = theNBPCount;
  2825.     theAFPInfo->uamType = theUAMType;
  2826.     theAFPInfo->zoneNameOffset = (short)((long)theAFPInfo->zoneName - (long)theAFPInfo);
  2827.     theAFPInfo->serverNameOffset = (short)((long)theAFPInfo->serverName - (long)theAFPInfo);
  2828.     theAFPInfo->volNameOffset = (short)((long)theAFPInfo->volName - (long)theAFPInfo);
  2829.     theAFPInfo->userNameOffset = (short)((long)theAFPInfo->userName - (long)theAFPInfo);
  2830.     theAFPInfo->userPasswordOffset = (short)((long)theAFPInfo->userPassword - (long)theAFPInfo);
  2831.     theAFPInfo->volPasswordOffset = (short)((long)theAFPInfo->volPassword - (long)theAFPInfo);
  2832.     
  2833.     BlockMoveData(theZoneName, theAFPInfo->zoneName, theZoneName[0] + 1);
  2834.     BlockMoveData(theServerName, theAFPInfo->serverName, theServerName[0] + 1);
  2835.     BlockMoveData(theVolName, theAFPInfo->volName, theVolName[0] + 1);
  2836.     BlockMoveData(theUserName, theAFPInfo->userName, theUserName[0] + 1);
  2837.     BlockMoveData(theUserPassWord, theAFPInfo->userPassword, theUserPassWord[0] + 1);
  2838.     BlockMoveData(theVolPassWord, theAFPInfo->volPassword, theVolPassWord[0] + 1);
  2839. }
  2840.  
  2841. /*****************************************************************************/
  2842.  
  2843. pascal    OSErr    RetrieveAFPVolMountInfo(AFPVolMountInfoPtr theAFPInfo,
  2844.                                         short *theFlags,
  2845.                                         short *theUAMType,
  2846.                                         StringPtr theZoneName,
  2847.                                         StringPtr theServerName,
  2848.                                         StringPtr theVolName,
  2849.                                         StringPtr theUserName)
  2850. {
  2851.     OSErr        error;
  2852.     StringPtr    tempPtr;
  2853.         
  2854.     /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
  2855.     if ( theAFPInfo->media == AppleShareMediaType )
  2856.     {
  2857.         *theFlags = theAFPInfo->flags;
  2858.         *theUAMType = theAFPInfo->uamType;
  2859.         
  2860.         tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->zoneNameOffset);
  2861.         BlockMoveData(tempPtr, theZoneName, tempPtr[0] + 1);
  2862.         
  2863.         tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->serverNameOffset);
  2864.         BlockMoveData(tempPtr, theServerName, tempPtr[0] + 1);
  2865.         
  2866.         tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->volNameOffset);
  2867.         BlockMoveData(tempPtr, theVolName, tempPtr[0] + 1);
  2868.         
  2869.         tempPtr = (StringPtr)((long)theAFPInfo + theAFPInfo->userNameOffset);
  2870.         BlockMoveData(tempPtr, theUserName, tempPtr[0] + 1);
  2871.         
  2872.         error = noErr;
  2873.     }
  2874.     else
  2875.     {
  2876.         error = paramErr;
  2877.     }
  2878.     
  2879.     return ( error );
  2880. }
  2881.  
  2882. /*****************************************************************************/
  2883.  
  2884. pascal    OSErr    GetUGEntries(short objType,
  2885.                              UGEntryPtr entries,
  2886.                              long reqEntryCount,
  2887.                              long *actEntryCount,
  2888.                              long *objID)
  2889. {
  2890.     HParamBlockRec pb;
  2891.     OSErr error = noErr;
  2892.     UGEntry *endEntryArray;
  2893.  
  2894.     pb.objParam.ioObjType = objType;
  2895.     *actEntryCount = 0;
  2896.     for ( endEntryArray = entries + reqEntryCount; (entries < endEntryArray) && (error == noErr); ++entries )
  2897.     {
  2898.         pb.objParam.ioObjNamePtr = (StringPtr)entries->name;
  2899.         pb.objParam.ioObjID = *objID;
  2900.         /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */
  2901.         /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */
  2902.         /* A CMovePBPtr works OK, but this will be changed in the future  back to */
  2903.         /* HParmBlkPtr, so I'm just casting it here. */
  2904.         error = PBGetUGEntrySync(&pb);
  2905.         if ( error == noErr )
  2906.         {
  2907.             entries->objID = *objID = pb.objParam.ioObjID;
  2908.             entries->objType = objType;
  2909.             ++*actEntryCount;
  2910.         }
  2911.     }
  2912.     
  2913.     return ( error );
  2914. }
  2915.  
  2916. /*****************************************************************************/
  2917.  
  2918.